1、为图层添加渲染器和读取器,分离图层基础信息、渲染和读写;

2、LayerTreeLayerNode持有MapLayer图层引用,为右键菜单做准备;
3、改名:imageviewer → mapcavas,mapcavas持有MapLayer图层引用,为刷新图像做准备;
This commit is contained in:
tangchao0503
2026-02-25 15:08:06 +08:00
parent e3b2d136d3
commit bdf956ed99
13 changed files with 659 additions and 286 deletions

View File

@ -265,29 +265,29 @@ sizePolicy1.setHeightForWidth(graphicsView_delete->sizePolicy().hasHeightForWidt
//dock_layers->setMaximumHeight(498);
//高光谱查看
QDockWidget* dock_hyperimgViewer = new CustomDockWidgetBase(QString::fromLocal8Bit("hyimgViewer"), this);
dock_hyperimgViewer->setObjectName("hyimgViewer");
//高光谱查看
QDockWidget* dock_hyperimgViewer = new CustomDockWidgetBase(QString::fromLocal8Bit("hyimgViewer"), this);
dock_hyperimgViewer->setObjectName("hyimgViewer");
QWidget* dock_hyperimgViewerWidgetContents = new QWidget();
dock_hyperimgViewerWidgetContents->setObjectName(QString::fromUtf8("dock_hyperimgViewerWidgetContents"));
QGridLayout* gridLayout_hyperimgViewer = new QGridLayout(dock_hyperimgViewerWidgetContents);
gridLayout_hyperimgViewer->setSpacing(6);
gridLayout_hyperimgViewer->setObjectName(QString::fromUtf8("gridLayout_hyperimgViewer"));
gridLayout_hyperimgViewer->setVerticalSpacing(6);
gridLayout_hyperimgViewer->setContentsMargins(1, 2, 1, 2);
QWidget* dock_hyperimgViewerWidgetContents = new QWidget();
dock_hyperimgViewerWidgetContents->setObjectName(QString::fromUtf8("dock_hyperimgViewerWidgetContents"));
QGridLayout* gridLayout_hyperimgViewer = new QGridLayout(dock_hyperimgViewerWidgetContents);
gridLayout_hyperimgViewer->setSpacing(6);
gridLayout_hyperimgViewer->setObjectName(QString::fromUtf8("gridLayout_hyperimgViewer"));
gridLayout_hyperimgViewer->setVerticalSpacing(6);
gridLayout_hyperimgViewer->setContentsMargins(1, 2, 1, 2);
m_imageViewerTabWidget = new QTabWidget();
m_imageViewerTabWidget = new QTabWidget();
//m_imageViewerTabWidget->tabBar()->setFixedHeight(40);//没有效果在qss中设置高度才有效果为啥
m_imageViewerTabWidget->clear();//必须放在最前面首先删除所有的tab
QToolButton* maxButton = new QToolButton();
maxButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarMaxButton));
connect(maxButton, SIGNAL(clicked()), dock_hyperimgViewer, SLOT(toggleMaximize()));
m_imageViewerTabWidget->setCornerWidget(maxButton, Qt::TopRightCorner);
QToolButton* maxButton = new QToolButton();
maxButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarMaxButton));
connect(maxButton, SIGNAL(clicked()), dock_hyperimgViewer, SLOT(toggleMaximize()));
m_imageViewerTabWidget->setCornerWidget(maxButton, Qt::TopRightCorner);
//onCreateTab(0);
//m_imageViewerTabWidget->setTabsClosable(true);//这样每页都会有关闭按钮
connect(m_imageViewerTabWidget, SIGNAL(currentChanged(int)), this, SLOT(onTabWidgetCurrentChanged(int)));
m_imageViewerTabWidget->setStyleSheet(R"(
connect(m_imageViewerTabWidget, SIGNAL(currentChanged(int)), this, SLOT(onTabWidgetCurrentChanged(int)));
m_imageViewerTabWidget->setStyleSheet(R"(
QTabBar::tab {
background: #0E1C4C;
color: white;
@ -676,7 +676,6 @@ void HPPA::initMenubarToolbar()
}
)");
QWidget* topWidget = new QWidget();
topWidget->setStyleSheet("background-color: #040125;");
QVBoxLayout* verticalLayout_topWidget = new QVBoxLayout(topWidget);
@ -934,8 +933,6 @@ void HPPA::createOneMotorScenario()
m_tabManager->showTab(m_omc);
m_view3DModelManager->switchScenario(View3DModelManager::ScenarioType::OneMotor);
//右上角轮播
@ -964,8 +961,6 @@ void HPPA::createPlantPhenotypeScenario()
m_tabManager->showTab(m_pc);
m_tabManager->showTab(m_tmc);
m_view3DModelManager->switchScenario(View3DModelManager::ScenarioType::PlantPhenotype);
//右上角轮播
@ -1078,7 +1073,7 @@ void HPPA::onStartRecordStep1()
m_RecordState -= 1;
ui.action_start_recording->setText(QString::fromLocal8Bit("采集"));
ui.mainToolBar->widgetForAction(ui.action_start_recording)->setStyleSheet("QWidget{background-color:rgb(0,255,0);}");
ui.mainToolBar->widgetForAction(ui.action_start_recording)->setStyleSheet("QWidget{background-color:rgb(0,255,0);}");
}
return;
}
@ -1124,42 +1119,41 @@ void HPPA::onStartRecordStep1()
void HPPA::onCreateTab(int trackNumber)
{
if (trackNumber >= 0)
{
m_numberOfRecording = trackNumber;
if (trackNumber >= 0)
{
m_numberOfRecording = trackNumber;
QWidget* tabTmp = new QWidget();
QWidget* tabTmp = new QWidget();
QGridLayout* GridLayout = new QGridLayout();
GridLayout->addWidget(new ImageViewer(tabTmp));
QGridLayout* GridLayout = new QGridLayout();
GridLayout->addWidget(new mapcavas(tabTmp));
tabTmp->setLayout(GridLayout);
tabTmp->setLayout(GridLayout);
m_imageViewerTabWidget->addTab(tabTmp, QString::number(trackNumber + 1));
m_imageViewerTabWidget->addTab(tabTmp, QString::number(trackNumber + 1));
m_imageViewerTabWidget->setCurrentIndex(trackNumber);
}
m_imageViewerTabWidget->setCurrentIndex(trackNumber);
}
}
void HPPA::onTabWidgetCurrentChanged(int index)//代码新建一个tab会调用onTabWidgetCurrentChanged函数
{
if (index < 0)//当删除所有tab时index=-1
{
return;
}
if (index < 0)//当删除所有tab时index=-1
{
return;
}
//记录当前
m_TabWidgetCurrentIndex = index;
//记录当前
m_TabWidgetCurrentIndex = index;
//获取绘图控件
QWidget* currentWidget = m_imageViewerTabWidget->widget(index);
QList<mapcavas*> currentImageViewer = currentWidget->findChildren<mapcavas*>();
//获取绘图控件
QWidget* currentWidget = m_imageViewerTabWidget->widget(index);
QList<ImageViewer*> currentImageViewer = currentWidget->findChildren<ImageViewer*>();
//先disconnect然后再connect否则每次切换一次都会connect一次会累积connect很多次
disconnect(currentImageViewer[0], SIGNAL(leftMouseButtonPressed(int, int)), this, SLOT(onLeftMouseButtonPressed(int, int)));
connect(currentImageViewer[0], SIGNAL(leftMouseButtonPressed(int, int)), this, SLOT(onLeftMouseButtonPressed(int, int)));
//先disconnect然后再connect否则每次切换一次都会connect一次会累积connect很多次
disconnect(currentImageViewer[0], SIGNAL(leftMouseButtonPressed(int, int)), this, SLOT(onLeftMouseButtonPressed(int, int)));
connect(currentImageViewer[0], SIGNAL(leftMouseButtonPressed(int, int)), this, SLOT(onLeftMouseButtonPressed(int, int)));
}
void HPPA::onActionOpenDirectory()
@ -1169,34 +1163,6 @@ void HPPA::onActionOpenDirectory()
QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(directory)));
//QDesktopServices::openUrl(QUrl("file:C:\ASD", QUrl::TolerantMode));
////std::cout << "保存影像文件!" << std::endl;
//
//FileOperation * fileOperation = new FileOperation();
//string directory = fileOperation->getDirectoryFromString();
//string sourceImg = directory + "\\tmp_image";
//string sourceHdr = sourceImg + ".hdr";
//
//QString FileName2Save = QFileDialog::getSaveFileName(this,tr("save image"));
//if (FileName2Save.length()>0)
//{
// string targetImg = FileName2Save.toStdString();
// string targetHdr = targetImg + ".hdr";
// emit CopyFileThreadSignal(QString::fromStdString(sourceHdr), QString::fromStdString(targetHdr));
// emit CopyFileThreadSignal(QString::fromStdString(sourceImg), QString::fromStdString(targetImg));
// this->setEnabled(false);
//}
//else
//{
// std::cout << "文件名长度:" << FileName2Save.length() << std::endl;
//}
}
@ -1274,6 +1240,7 @@ void HPPA::onLeftMouseButtonPressed(int x, int y)
QLineSeries* series = new QLineSeries();
//series->clear();//////////////////////////////
QString imagerSelected = mImagerGroup->checkedAction()->objectName();
if (imagerSelected == "mActionPica_L" || imagerSelected == "mActionCorning_410" || imagerSelected == "mActionPika_XC2")
{
@ -1385,7 +1352,6 @@ void HPPA::onPlotRgbImage()
int height = m_cam_label->height();
QPixmap fitpixmap = pixmap.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation); //按比例缩放
m_cam_label->setPixmap(fitpixmap);
}
@ -1444,6 +1410,53 @@ void HPPA::onExit()
void HPPA::onOpenImg()
{
// 1) 弹出文件对话框获取uri
QString uri = QFileDialog::getOpenFileName(this, tr("Open Image"), QString(), tr("Raster Files (*.bil *.hdr *.tif *.tiff *.img *.raw);;All Files (*)"));
if (uri.isEmpty())
return;
// 2) 创建 RasterLayer然后添加到图层管理器
if (!m_LayerTreeModel || !m_RasterGroup)
{
QMessageBox::warning(this, tr("Error"), tr("Layer model is not initialized."));
return;
}
QFileInfo fi(uri);
QString baseName = fi.completeBaseName();
// Create RasterLayer and wrap in LayerTreeLayerNode
RasterLayer* rl = new RasterLayer(baseName, uri);
auto* layerNode = new LayerTreeLayerNode(rl);
m_LayerTreeModel->addLayer(m_RasterGroup, layerNode);
// 3) 渲染一副图像并保存到 C:\2
RasterLayer::RenderParams params; // use defaults
QImage img = rl->render(params);
if (img.isNull())
{
QMessageBox::warning(this, tr("Render failed"), tr("Failed to render image from %1").arg(uri));
return;
}
QDir outDir("C:/2");
if (!outDir.exists())
{
if (!outDir.mkpath("."))
{
QMessageBox::warning(this, tr("Save failed"), tr("Failed to create output directory C:/2"));
return;
}
}
QString outPath = outDir.filePath(baseName + "_render.png");
if (!img.save(outPath))
{
QMessageBox::warning(this, tr("Save failed"), tr("Failed to save rendered image to %1").arg(outPath));
return;
}
QMessageBox::information(this, tr("Saved"), tr("Rendered image saved to %1").arg(outPath));
}
void HPPA::onconnect()
@ -1638,7 +1651,7 @@ void HPPA::onFocus2(int command)
QWidget* tabTmp = new QWidget();
QGridLayout* GridLayout = new QGridLayout();
GridLayout->addWidget(new ImageViewer(tabTmp));
GridLayout->addWidget(new mapcavas(tabTmp));
tabTmp->setLayout(GridLayout);
@ -1726,28 +1739,28 @@ void HPPA::recordWhiteFinish()
void HPPA::onPlotHyperspectralImageRgbImage(int fileNumber, int frameNumber)
{
//使用机械臂采集时,会在停止采集后的瞬间又开始采集,会导致上次采集最后发射的信号调用此槽函数报错
QAction* checked = moveplatformActionGroup->checkedAction();
QString checkedName = checked->objectName();
if (frameNumber == -1 && checkedName == "mAction_RobotArm")
{
return;
}
//使用机械臂采集时,会在停止采集后的瞬间又开始采集,会导致上次采集最后发射的信号调用此槽函数报错
QAction* checked = moveplatformActionGroup->checkedAction();
QString checkedName = checked->objectName();
if (frameNumber == -1 && checkedName == "mAction_RobotArm")
{
return;
}
//return;
//获取绘图控件
QWidget* currentWidget = m_imageViewerTabWidget->widget(fileNumber);
QList<ImageViewer*> currentImageViewer = currentWidget->findChildren<ImageViewer*>();
currentImageViewer[0]->DisplayFrameNumber(m_Imager->getFrameCounter());//界面中显示已经采集的帧数
//return;
//获取绘图控件
QWidget* currentWidget = m_imageViewerTabWidget->widget(fileNumber);
QList<mapcavas*> currentImageViewer = currentWidget->findChildren<mapcavas*>();
currentImageViewer[0]->DisplayFrameNumber(m_Imager->getFrameCounter());//界面中显示已经采集的帧数
//创建需要显示的图像--opencv版本
ImageProcessor imageProcessor;
//cv::Mat rgbImage(*m_Imager->getRgbImage()->m_matRgbImage, cv::Range(0, m_Imager->getFrameCounter()), cv::Range::all());//2022.3.18重构的
cv::Mat rgbImage(*m_Imager->getMatRgbImage(), cv::Range(0, m_Imager->getFrameCounter()), cv::Range::all());
cv::Mat rgbImageStretched = imageProcessor.CStretch(rgbImage, 0.02);
//创建需要显示的图像--opencv版本
ImageProcessor imageProcessor;
//cv::Mat rgbImage(*m_Imager->getRgbImage()->m_matRgbImage, cv::Range(0, m_Imager->getFrameCounter()), cv::Range::all());//2022.3.18重构的
cv::Mat rgbImage(*m_Imager->getMatRgbImage(), cv::Range(0, m_Imager->getFrameCounter()), cv::Range::all());
cv::Mat rgbImageStretched = imageProcessor.CStretch(rgbImage, 0.02);
//在界面中显示图像
currentImageViewer[0]->SetImage(&QPixmap::fromImage(imageProcessor.Mat2QImage(rgbImageStretched)));//绘制图像
//在界面中显示图像
currentImageViewer[0]->SetImage(&QPixmap::fromImage(imageProcessor.Mat2QImage(rgbImageStretched)));//绘制图像
//20241225
//保存拉伸后的rgb
@ -1768,12 +1781,12 @@ void HPPA::onPlotHyperspectralImageRgbImage(int fileNumber, int frameNumber)
void HPPA::PlotSpectral(int state)
{
if (state == 1)
{
//显示影像
QWidget* currentWidget = m_imageViewerTabWidget->currentWidget();
QList<ImageViewer*> currentImageViewer = currentWidget->findChildren<ImageViewer*>();
currentImageViewer[0]->DisplayFrameNumber(m_Imager->getFocusFrameCounter());//界面中显示已经采集的帧数
if (state == 1)
{
//显示影像
QWidget* currentWidget = m_imageViewerTabWidget->currentWidget();
QList<mapcavas*> currentImageViewer = currentWidget->findChildren<mapcavas*>();
currentImageViewer[0]->DisplayFrameNumber(m_Imager->getFocusFrameCounter());//界面中显示已经采集的帧数
ImageProcessor imageProcessor;
//cv::Mat grayImage(*m_Imager->getRgbImage()->m_matFocusGrayImage, cv::Range::all(), cv::Range::all());//2022.3.18重构的
@ -1782,26 +1795,26 @@ void HPPA::PlotSpectral(int state)
currentImageViewer[0]->SetImage(&QPixmap::fromImage(m_Imager->getQImageFocusGrayImage()));//绘制图像
//绘制光谱
QLineSeries* series = new QLineSeries();
//series->clear();//////////////////////////////
int sampleCount = m_Imager->getSampleCount();
for (size_t i = 0; i < sampleCount; i++)
{
//malloc申请的内存用法1可以当做数组用
//series->append(i, m_Imager->buffer[i + 5 * 900]);
//series->append(i, m_Imager->buffer[900 * 150 + i]);
series->append(i, m_Imager->buffer[1368 * 150 + i]);
}
//绘制光谱
QLineSeries* series = new QLineSeries();
//series->clear();//////////////////////////////
int sampleCount = m_Imager->getSampleCount();
for (size_t i = 0; i < sampleCount; i++)
{
//malloc申请的内存用法1可以当做数组用
//series->append(i, m_Imager->buffer[i + 5 * 900]);
//series->append(i, m_Imager->buffer[900 * 150 + i]);
series->append(i, m_Imager->buffer[1368 * 150 + i]);
}
QChart* chart = new QChart();
chart->legend()->hide();
chart->addSeries(series);
chart->createDefaultAxes();
//chart->setTitle("Simple line chart example");
QChart* chart = new QChart();
chart->legend()->hide();
chart->addSeries(series);
chart->createDefaultAxes();
//chart->setTitle("Simple line chart example");
m_chartView->setChart(chart);
}
m_chartView->setChart(chart);
}
else
{
//改变界面上的按钮文字

View File

@ -125,7 +125,9 @@
<ClCompile Include="PowerControl.cpp" />
<ClCompile Include="QDoubleSlider.cpp" />
<ClCompile Include="QMotorDoubleSlider.cpp" />
<ClCompile Include="RasterDataProvider.cpp" />
<ClCompile Include="RasterLayer.cpp" />
<ClCompile Include="RasterRenderer.cpp" />
<ClCompile Include="resononImager.cpp" />
<ClCompile Include="ResononNirImager.cpp" />
<ClCompile Include="RgbCameraOperation.cpp" />
@ -194,6 +196,8 @@
<QtMoc Include="LayerTreeLayerNode.h" />
<QtMoc Include="MapLayer.h" />
<QtMoc Include="RasterLayer.h" />
<ClInclude Include="RasterDataProvider.h" />
<ClInclude Include="RasterRenderer.h" />
<ClInclude Include="utility_tc.h" />
<QtMoc Include="aboutWindow.h" />
<ClInclude Include="hppaConfigFile.h" />

View File

@ -160,6 +160,12 @@
<ClCompile Include="RasterLayer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RasterDataProvider.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RasterRenderer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<QtMoc Include="fileOperation.h">
@ -287,6 +293,12 @@
<ClInclude Include="irisximeaimager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RasterDataProvider.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RasterRenderer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<QtUic Include="FocusDialog.ui">

View File

@ -5,240 +5,217 @@
#include <QPoint>
#include "ImageViewer.h"
#include "RasterLayer.h"
#define VIEW_CENTER viewport()->rect().center()
#define VIEW_WIDTH viewport()->rect().width()
#define VIEW_HEIGHT viewport()->rect().height()
ImageViewer::ImageViewer(QWidget* pParent) :QGraphicsView(pParent)
mapcavas::mapcavas(QWidget* pParent) :QGraphicsView(pParent)
{
setRenderHint(QPainter::Antialiasing);
setRenderHint(QPainter::SmoothPixmapTransform);
setDragMode(QGraphicsView::ScrollHandDrag);
setRenderHint(QPainter::Antialiasing);
setRenderHint(QPainter::SmoothPixmapTransform);
setDragMode(QGraphicsView::ScrollHandDrag);
// <20>ؼ<EFBFBD><D8BC><EFBFBD><E3A3BA>ʹ<EFBFBD><EFBFBD> Qt Ĭ<>ϵ<EFBFBD> anchor<6F><72><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Լ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
setTransformationAnchor(QGraphicsView::NoAnchor);
setResizeAnchor(QGraphicsView::NoAnchor);
// ʹ<EFBFBD><EFBFBD> Qt Ĭ<><C4AC> anchor <EFBFBD><EFBFBD>Ϊ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
setTransformationAnchor(QGraphicsView::NoAnchor);
setResizeAnchor(QGraphicsView::NoAnchor);
m_qtGraphicsScene = new QGraphicsScene(this);
this->setScene(m_qtGraphicsScene);
m_qtGraphicsScene->setSceneRect(-1e6, -1e6, 2e6, 2e6);
m_qtGraphicsScene = new QGraphicsScene(this);
this->setScene(m_qtGraphicsScene);
m_qtGraphicsScene->setSceneRect(-1e6, -1e6, 2e6, 2e6);
m_framNumberLabel = new QLabel(this);
m_framNumberLabel->setAlignment(Qt::AlignHCenter);
m_framNumberLabel->setAlignment(Qt::AlignVCenter);
m_framNumberLabel = new QLabel(this);
m_framNumberLabel->setAlignment(Qt::AlignHCenter);
m_framNumberLabel->setAlignment(Qt::AlignVCenter);
QFont ft;
ft.setPointSize(14);
m_framNumberLabel->setFont(ft);
QFont ft;
ft.setPointSize(14);
m_framNumberLabel->setFont(ft);
m_framNumberLabel->setText("0");
m_GraphicsPixmapItemHandle = nullptr;
m_GraphicsPixmapItemHandle = nullptr;
m_scale = 1.0;
m_zoomDelta = 0.1;
m_translateSpeed = 1.0;
m_bMouseTranslate = false;
m_scale = 1.0;
m_zoomDelta = 0.1;
m_translateSpeed = 1.0;
m_bMouseTranslate = false;
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setFrameShape(QFrame::NoFrame);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setFrameShape(QFrame::NoFrame);
}
ImageViewer::~ImageViewer()
mapcavas::~mapcavas()
{
}
void ImageViewer::DisplayFrameNumber(int frameNumber)
void mapcavas::DisplayFrameNumber(int frameNumber)
{
m_framNumberLabel->setText(QString::number(frameNumber));
m_framNumberLabel->adjustSize();
m_framNumberLabel->setText(QString::number(frameNumber));
m_framNumberLabel->adjustSize();
}
void ImageViewer::SetImage(QPixmap *image)
void mapcavas::SetImage(QPixmap *image)
{
if (!HasImage())
{
m_GraphicsPixmapItemHandle = m_qtGraphicsScene->addPixmap(*image);
}
else
{
m_GraphicsPixmapItemHandle->setPixmap(*image);
}
ensureSceneVisible();
if (!HasImage())
{
m_GraphicsPixmapItemHandle = m_qtGraphicsScene->addPixmap(*image);
}
else
{
m_GraphicsPixmapItemHandle->setPixmap(*image);
}
ensureSceneVisible();
}
void ImageViewer::ensureSceneVisible()
void mapcavas::ensureSceneVisible()
{
resetTransform();
resetTransform();
auto view_rect = viewport()->rect();
auto scene_rect = this->scene()->itemsBoundingRect();
auto view_rect = viewport()->rect();
auto scene_rect = this->scene()->itemsBoundingRect();
double x_ratio = view_rect.width() / scene_rect.width();
double y_ratio = view_rect.height() / scene_rect.height();
double scale_factor = std::min(x_ratio, y_ratio) * 0.9;
double x_ratio = view_rect.width() / scene_rect.width();
double y_ratio = view_rect.height() / scene_rect.height();
double scale_factor = std::min(x_ratio, y_ratio) * 0.9;
scale(scale_factor, scale_factor);
m_scale *= scale_factor;
scale(scale_factor, scale_factor);
m_scale *= scale_factor;
centerOn(scene_rect.center());
centerOn(scene_rect.center());
}
bool ImageViewer::HasImage()
bool mapcavas::HasImage()
{
if (m_GraphicsPixmapItemHandle == nullptr)
{
return false;
}
else
{
return true;
}
if (m_GraphicsPixmapItemHandle == nullptr)
{
return false;
}
else
{
return true;
}
}
void ImageViewer::wheelEvent(QWheelEvent *event)
void mapcavas::wheelEvent(QWheelEvent *event)
{
//qDebug() << "---------------+++++++++++++++++++++++++++++++++++++++++++++++++++ ";
//if (true)//HasImage()
//{
// //Χ<><CEA7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŵ<EFBFBD><C5B4><EFBFBD>https://blog.csdn.net/GoForwardToStep/article/details/77035287?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param
// // <20><>ȡ<EFBFBD><C8A1>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>view<65><77>λ<EFBFBD><CEBB>;
// QPointF cursorPoint = event->pos();
// // <20><>ȡ<EFBFBD><C8A1>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>scene<6E><65>λ<EFBFBD><CEBB>;
// QPointF scenePos = this->mapToScene(QPoint(cursorPoint.x(), cursorPoint.y()));
if (HasImage())
{
QPointF oldPos = mapToScene(event->pos());
// // <20><>ȡview<65>Ŀ<EFBFBD><C4BF><EFBFBD>;
// qreal viewWidth = this->viewport()->width();
// qreal viewHeight = this->viewport()->height();
QPoint scrollAmount = event->angleDelta();
scrollAmount.y() > 0 ? zoomIn() : zoomOut();
// // <20><>ȡ<EFBFBD><C8A1>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>λ<EFBFBD><CEBB><EFBFBD><EFBFBD><E0B5B1>view<65><77>С<EFBFBD>ĺ<EFBFBD><C4BA>ݱ<EFBFBD><DDB1><EFBFBD>;
// qreal hScale = cursorPoint.x() / viewWidth;
// qreal vScale = cursorPoint.y() / viewHeight;
QPointF newPos = mapToScene(event->pos());
// // <20><><EFBFBD>ֵĹ<D6B5><C4B9><EFBFBD><EFBFBD><EFBFBD>
// QPoint scrollAmount = event->angleDelta();
// // <20><>ֵ<EFBFBD><D6B5>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6>ʹ<EFBFBD><CAB9><EFBFBD>߷Ŵ<DFB7><C5B4><EFBFBD>ֵ<EFBFBD><D6B5>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С
// scrollAmount.y() > 0 ? zoomIn() : zoomOut();
// // <20><>scene<6E><65><EFBFBD><EFBFBD>ת<EFBFBD><D7AA>Ϊ<EFBFBD>Ŵ<EFBFBD><C5B4><EFBFBD>С<EFBFBD><D0A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>;
// QPointF viewPoint = this->matrix().map(scenePos);
// // ͨ<><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>view<65>Ŵ<EFBFBD><C5B4><EFBFBD>С<EFBFBD><D0A1><EFBFBD><EFBFBD>չʾscene<6E><65>λ<EFBFBD><CEBB>;
// horizontalScrollBar()->setValue(int(viewPoint.x() - viewWidth * hScale));
// verticalScrollBar()->setValue(int(viewPoint.y() - viewHeight * vScale));
//}
if (HasImage())
{
QPointF oldPos = mapToScene(event->pos());
QPoint scrollAmount = event->angleDelta();
scrollAmount.y() > 0 ? zoomIn() : zoomOut();
QPointF newPos = mapToScene(event->pos());
QPointF delta = newPos - oldPos;
translate(delta.x(), delta.y());
}
//QGraphicsView::wheelEvent(event);//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
QPointF delta = newPos - oldPos;
translate(delta.x(), delta.y());
}
}
void ImageViewer::scaling(qreal scaleFactor)
void mapcavas::scaling(qreal scaleFactor)
{
//qDebug() << this->sceneRect();
scale(scaleFactor, scaleFactor);
scale(scaleFactor, scaleFactor);
}
void ImageViewer::mousePressEvent(QMouseEvent *event)
void mapcavas::mousePressEvent(QMouseEvent *event)
{
if (event->button()==Qt::LeftButton)
{
m_bMouseTranslate = true;
m_lastMousePos = event->pos();
if (event->button()==Qt::LeftButton)
{
m_bMouseTranslate = true;
m_lastMousePos = event->pos();
//qDebug() << mapToScene(m_lastMousePos);
emit leftMouseButtonPressed(mapToScene(m_lastMousePos).x(), mapToScene(m_lastMousePos).y());
}
//If you do not perform all the necessary work in your implementation of the virtual function, you may need to call the base class's implementation.
QGraphicsView::mousePressEvent(event);
emit leftMouseButtonPressed(mapToScene(m_lastMousePos).x(), mapToScene(m_lastMousePos).y());
}
QGraphicsView::mousePressEvent(event);
}
void ImageViewer::mouseMoveEvent(QMouseEvent *event)
void mapcavas::mouseMoveEvent(QMouseEvent *event)
{
if (m_bMouseTranslate){
QPointF mouseDelta = mapToScene(event->pos()) - mapToScene(m_lastMousePos);
translate(mouseDelta.x(),mouseDelta.y());
}
if (m_bMouseTranslate){
QPointF mouseDelta = mapToScene(event->pos()) - mapToScene(m_lastMousePos);
translate(mouseDelta.x(),mouseDelta.y());
}
m_lastMousePos = event->pos();
QGraphicsView::mousePressEvent(event);
m_lastMousePos = event->pos();
QGraphicsView::mousePressEvent(event);
}
void ImageViewer::mouseReleaseEvent(QMouseEvent *event)
void mapcavas::mouseReleaseEvent(QMouseEvent *event)
{
m_bMouseTranslate = false;
QGraphicsView::mouseReleaseEvent(event);
m_bMouseTranslate = false;
QGraphicsView::mouseReleaseEvent(event);
}
void ImageViewer::mouseDoubleClickEvent(QMouseEvent *event)
void mapcavas::mouseDoubleClickEvent(QMouseEvent *event)
{
QGraphicsView::mouseDoubleClickEvent(event);
QGraphicsView::mouseDoubleClickEvent(event);
}
void ImageViewer::zoomIn()
void mapcavas::zoomIn()
{
zoom(1 + m_zoomDelta);
zoom(1 + m_zoomDelta);
}
void ImageViewer::zoomOut()
void mapcavas::zoomOut()
{
zoom(1 - m_zoomDelta);
zoom(1 - m_zoomDelta);
}
void ImageViewer::zoom(float scaleFactor)
void mapcavas::zoom(float scaleFactor)
{
// <20><>ֹ<EFBFBD><D6B9>С<EFBFBD><D0A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
if (factor < 0.07 || factor > 100)
return;
qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
if (factor < 0.07 || factor > 100)
return;
scale(scaleFactor, scaleFactor);
m_scale *= scaleFactor;
scale(scaleFactor, scaleFactor);
m_scale *= scaleFactor;
}
void ImageViewer::setTranslateSpeed(qreal speed)
void mapcavas::setTranslateSpeed(qreal speed)
{
// <20><><EFBFBD><EFBFBD><EFBFBD>ٶȷ<D9B6>Χ
Q_ASSERT_X(speed >= 0.0 && speed <= 2.0,
"InteractiveView::setTranslateSpeed", "Speed should be in range [0.0, 2.0].");
m_translateSpeed = speed;
Q_ASSERT_X(speed >= 0.0 && speed <= 2.0,
"InteractiveView::setTranslateSpeed", "Speed should be in range [0.0, 2.0].");
m_translateSpeed = speed;
}
qreal ImageViewer::translateSpeed() const
qreal mapcavas::translateSpeed() const
{
return m_translateSpeed;
return m_translateSpeed;
}
void ImageViewer::setZoomDelta(qreal delta)
void mapcavas::setZoomDelta(qreal delta)
{
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Χ
Q_ASSERT_X(delta >= 0.0 && delta <= 1.0,
"InteractiveView::setZoomDelta", "Delta should be in range [0.0, 1.0].");
m_zoomDelta = delta;
Q_ASSERT_X(delta >= 0.0 && delta <= 1.0,
"InteractiveView::setZoomDelta", "Delta should be in range [0.0, 1.0].");
m_zoomDelta = delta;
}
qreal ImageViewer::zoomDelta() const
qreal mapcavas::zoomDelta() const
{
return m_zoomDelta;
return m_zoomDelta;
}
// new: set associated raster layer
void mapcavas::setLayers(RasterLayer* layer)
{
m_rasterLayer = layer;
}
// new: refresh the map by rendering using the RasterLayer's render method
void mapcavas::freshmap()
{
if (!m_rasterLayer) return;
RasterLayer::RenderParams params; // default params; could be extended
QImage img = m_rasterLayer->render(params);
if (img.isNull()) return;
QPixmap pm = QPixmap::fromImage(img);
SetImage(&pm);
}

View File

@ -1,15 +1,18 @@
#ifndef IMAGE_VIEWER
#define IMAGE_VIEWER
#ifndef MAPCAVAS_H
#define MAPCAVAS_H
#include "QGraphicsView"
#include "qlabel.h"
class ImageViewer :public QGraphicsView
class RasterLayer; // forward
class mapcavas : public QGraphicsView
{
Q_OBJECT
public:
ImageViewer(QWidget* pParent = NULL);
~ImageViewer();
mapcavas(QWidget* pParent = NULL);
~mapcavas();
void DisplayFrameNumber(int frameNumber);
@ -38,12 +41,18 @@ public:
// <20><><EFBFBD>ŵ<EFBFBD><C5B5><EFBFBD><EFBFBD><EFBFBD>
void setZoomDelta(qreal delta);
qreal zoomDelta() const;
// new: set raster layer and refresh map
void setLayers(RasterLayer* layer);
void freshmap();
protected:
QGraphicsScene *m_qtGraphicsScene;
private:
QGraphicsPixmapItem *m_GraphicsPixmapItemHandle;
QLabel *m_framNumberLabel;//<2F><>ʾʵʱ<CAB5>ɼ<EFBFBD><C9BC><EFBFBD><EFBFBD><EFBFBD>֡<EFBFBD><D6A1>
RasterLayer* m_rasterLayer = nullptr; // associated raster layer
qreal m_translateSpeed; // ƽ<><C6BD><EFBFBD>ٶ<EFBFBD>
qreal m_zoomDelta; // <20><><EFBFBD>ŵ<EFBFBD><C5B5><EFBFBD><EFBFBD><EFBFBD>

View File

@ -4,3 +4,19 @@ LayerTreeLayerNode::LayerTreeLayerNode(MapLayer* layer, QObject* parent)
: LayerTreeNode(layer ? layer->name() : QString(), parent), m_layer(layer)
{
}
LayerTreeNode::Type LayerTreeLayerNode::type() const
{
return Type::Layer;
}
// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> MapLayer ָ<><EFBFBD><EBA3A8>ӵ<EFBFBD>У<EFBFBD>
void LayerTreeLayerNode::setMapLayer(MapLayer* layer)
{
m_layer = layer;
}
MapLayer* LayerTreeLayerNode::mapLayer() const
{
return m_layer;
}

View File

@ -7,14 +7,13 @@ class LayerTreeLayerNode : public LayerTreeNode
{
Q_OBJECT
public:
explicit LayerTreeLayerNode(MapLayer* layer,
QObject* parent = nullptr);
explicit LayerTreeLayerNode(MapLayer* layer, QObject* parent = nullptr);
Type type() const override { return Type::Layer; }
Type type() const override;
// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> MapLayer ָ<><EFBFBD><EBA3A8>ӵ<EFBFBD>У<EFBFBD>
void setMapLayer(MapLayer* layer) { m_layer = layer; }
MapLayer* mapLayer() const { return m_layer; }
void setMapLayer(MapLayer* layer);
MapLayer* mapLayer() const;
private:
MapLayer* m_layer = nullptr;

123
HPPA/RasterDataProvider.cpp Normal file
View File

@ -0,0 +1,123 @@
#include "RasterDataProvider.h"
#include <QString>
#include <QDebug>
#if HPPA_HAVE_GDAL
#include <gdal_priv.h>
#include <cpl_conv.h>
#endif
RasterDataProvider::RasterDataProvider(const QString& uri)
: m_uri(uri), m_dataset(nullptr)
{
}
RasterDataProvider::~RasterDataProvider()
{
close();
}
bool RasterDataProvider::open()
{
#if HPPA_HAVE_GDAL
GDALAllRegister();
m_dataset = (GDALDataset*)GDALOpen((const char*)m_uri.toLocal8Bit().constData(), GA_ReadOnly);
if (!m_dataset) {
qWarning() << "RasterDataProvider: failed to open dataset:" << m_uri;
return false;
}
return true;
#else
Q_UNUSED(m_uri);
qWarning() << "RasterDataProvider: GDAL not available, open will fail.";
return false;
#endif
}
void RasterDataProvider::close()
{
#if HPPA_HAVE_GDAL
if (m_dataset) {
GDALClose((GDALDatasetH)m_dataset);
m_dataset = nullptr;
}
#else
m_dataset = nullptr;
#endif
}
int RasterDataProvider::bandCount() const
{
#if HPPA_HAVE_GDAL
if (!m_dataset) return 0;
return m_dataset->GetRasterCount();
#else
Q_UNUSED(this);
return 0;
#endif
}
int RasterDataProvider::width() const
{
#if HPPA_HAVE_GDAL
if (!m_dataset) return 0;
return m_dataset->GetRasterXSize();
#else
Q_UNUSED(this);
return 0;
#endif
}
int RasterDataProvider::height() const
{
#if HPPA_HAVE_GDAL
if (!m_dataset) return 0;
return m_dataset->GetRasterYSize();
#else
Q_UNUSED(this);
return 0;
#endif
}
std::vector<double> RasterDataProvider::bandWavelengths() const
{
std::vector<double> res;
#if HPPA_HAVE_GDAL
if (!m_dataset) return res;
for (int i = 1; i <= m_dataset->GetRasterCount(); ++i) {
GDALRasterBand* band = m_dataset->GetRasterBand(i);
if (!band) continue;
// Attempt to read wavelength from metadata (commonly "Wavelength" or "wavelength")
const char* val = band->GetMetadataItem("Wavelength");
if (!val) val = band->GetMetadataItem("wavelength");
if (val) {
double v = atof(val);
res.push_back(v);
} else {
// push a negative to indicate unknown
res.push_back(-1.0);
}
}
#endif
return res;
}
bool RasterDataProvider::readBandAsFloat(int bandIndex, std::vector<float>& outBuffer) const
{
#if HPPA_HAVE_GDAL
if (!m_dataset) return false;
int bands = m_dataset->GetRasterCount();
if (bandIndex < 0 || bandIndex >= bands) return false;
GDALRasterBand* band = m_dataset->GetRasterBand(bandIndex + 1);
if (!band) return false;
int w = m_dataset->GetRasterXSize();
int h = m_dataset->GetRasterYSize();
outBuffer.assign(w * h, 0.0f);
CPLErr err = band->RasterIO(GF_Read, 0, 0, w, h, outBuffer.data(), w, h, GDT_Float32, 0, 0);
return err == CE_None;
#else
Q_UNUSED(bandIndex);
Q_UNUSED(outBuffer);
return false;
#endif
}

44
HPPA/RasterDataProvider.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include <QString>
#include <vector>
#if __has_include(<gdal_priv.h>)
#define HPPA_HAVE_GDAL 1
#include <gdal_priv.h>
#include <cpl_conv.h>
#else
#define HPPA_HAVE_GDAL 0
#endif
class RasterDataProvider
{
public:
explicit RasterDataProvider(const QString& uri);
~RasterDataProvider();
bool open();
void close();
int bandCount() const;
int width() const;
int height() const;
// Returns per-band wavelength metadata if available. If not available, returns empty vector.
std::vector<double> bandWavelengths() const;
// Read a single band (0-based index) into a float buffer of size width()*height().
// Returns true on success.
bool readBandAsFloat(int bandIndex, std::vector<float>& outBuffer) const;
QString uri() const { return m_uri; }
private:
QString m_uri;
#if HPPA_HAVE_GDAL
GDALDataset* m_dataset = nullptr;
#else
// no-op when GDAL not available
void* m_dataset = nullptr;
#endif
};

View File

@ -1,12 +1,48 @@
#include "RasterLayer.h"
#include "RasterDataProvider.h"
#include "RasterRenderer.h"
RasterLayer::RasterLayer(const QString& name, const QString& uri)
: MapLayer(name, uri)
{
// lazy creation
}
MapLayer::LayerType RasterLayer::layerType() const
{
return MapLayer::LayerType::Raster;
}
RasterDataProvider* RasterLayer::dataProvider() const
{
return m_provider ? m_provider.get() : nullptr;
}
RasterRenderer* RasterLayer::renderer() const
{
return m_renderer ? m_renderer.get() : nullptr;
}
bool RasterLayer::openDataProvider()
{
if (!m_provider) m_provider = std::make_unique<RasterDataProvider>(dataPath());
if (!m_provider) return false;
bool ok = m_provider->open();
if (ok && !m_renderer) m_renderer = std::make_unique<RasterRenderer>(m_provider.get());
return ok;
}
QImage RasterLayer::render(const RenderParams& params)
{
if (!m_provider) {
if (!openDataProvider()) return QImage();
}
if (!m_renderer) m_renderer = std::make_unique<RasterRenderer>(m_provider.get());
RasterRenderer::Params p;
p.rWave = params.rWave;
p.gWave = params.gWave;
p.bWave = params.bWave;
p.minValue = params.minValue;
p.maxValue = params.maxValue;
return m_renderer->render(p);
}

View File

@ -1,6 +1,11 @@
#pragma once
#include "MapLayer.h"
#include <memory>
#include <QImage>
class RasterDataProvider;
class RasterRenderer;
class RasterLayer : public MapLayer
{
@ -10,5 +15,25 @@ public:
LayerType layerType() const override;
// future: renderer, data provider, etc.
// Access provider/renderer
RasterDataProvider* dataProvider() const;
RasterRenderer* renderer() const;
// Create or open provider based on this layer's uri
bool openDataProvider();
struct RenderParams {
double rWave = 665.0; // default wavelengths (nm)
double gWave = 560.0;
double bWave = 490.0;
double minValue = 0.0; // optional stretch
double maxValue = 255.0;
};
// Render the raster using current provider and renderer. Returns an empty QImage on failure.
QImage render(const RenderParams& params);
private:
std::unique_ptr<RasterDataProvider> m_provider;
std::unique_ptr<RasterRenderer> m_renderer;
};

86
HPPA/RasterRenderer.cpp Normal file
View File

@ -0,0 +1,86 @@
#include "RasterRenderer.h"
#include "RasterDataProvider.h"
#include <QDebug>
#include <algorithm>
RasterRenderer::RasterRenderer(RasterDataProvider* provider)
: m_provider(provider)
{
}
void RasterRenderer::stretchTo8bit(const std::vector<float>& in, std::vector<unsigned char>& out, float minVal, float maxVal)
{
size_t n = in.size();
out.resize(n);
if (maxVal <= minVal) {
std::fill(out.begin(), out.end(), 0);
return;
}
float denom = 1.0f / (maxVal - minVal);
for (size_t i = 0; i < n; ++i) {
float v = (in[i] - minVal) * denom;
v = std::min(std::max(v, 0.0f), 1.0f);
out[i] = static_cast<unsigned char>(v * 255.0f);
}
}
QImage RasterRenderer::render(const Params& params)
{
if (!m_provider) return QImage();
int bands = m_provider->bandCount();
int w = m_provider->width();
int h = m_provider->height();
if (w <= 0 || h <= 0) return QImage();
// Find nearest bands for requested wavelengths if wavelengths available
std::vector<double> wavelengths = m_provider->bandWavelengths();
auto chooseBandIndexForWave = [&](double wave)->int {
if (wavelengths.empty()) {
// fallback: select R,G,B as first three bands
if (bands >= 3) return (wave==params.rWave?0:(wave==params.gWave?1:2));
if (bands >= 1) return 0;
return -1;
}
int best = -1; double bestDiff = 1e12;
for (int i = 0; i < (int)wavelengths.size(); ++i) {
if (wavelengths[i] < 0) continue;
double d = std::abs(wavelengths[i] - wave);
if (d < bestDiff) { bestDiff = d; best = i; }
}
if (best >= 0) return best;
// fallback
return std::min(2, bands-1);
};
int rIdx = chooseBandIndexForWave(params.rWave);
int gIdx = chooseBandIndexForWave(params.gWave);
int bIdx = chooseBandIndexForWave(params.bWave);
std::vector<float> rbuf, gbuf, bbuf;
if (rIdx >= 0) m_provider->readBandAsFloat(rIdx, rbuf);
if (gIdx >= 0) m_provider->readBandAsFloat(gIdx, gbuf);
if (bIdx >= 0) m_provider->readBandAsFloat(bIdx, bbuf);
std::vector<unsigned char> r8, g8, b8;
float minV = static_cast<float>(params.minValue);
float maxV = static_cast<float>(params.maxValue);
if (!rbuf.empty()) stretchTo8bit(rbuf, r8, minV, maxV);
if (!gbuf.empty()) stretchTo8bit(gbuf, g8, minV, maxV);
if (!bbuf.empty()) stretchTo8bit(bbuf, b8, minV, maxV);
QImage out(w, h, QImage::Format_RGB888);
for (int y = 0; y < h; ++y) {
unsigned char* scan = out.scanLine(y);
for (int x = 0; x < w; ++x) {
int idx = y * w + x;
unsigned char rc = (r8.size() > (size_t)idx) ? r8[idx] : 0;
unsigned char gc = (g8.size() > (size_t)idx) ? g8[idx] : 0;
unsigned char bc = (b8.size() > (size_t)idx) ? b8[idx] : 0;
scan[x*3 + 0] = rc;
scan[x*3 + 1] = gc;
scan[x*3 + 2] = bc;
}
}
return out;
}

29
HPPA/RasterRenderer.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <QImage>
#include <vector>
class RasterDataProvider;
class RasterRenderer
{
public:
struct Params {
double rWave = 665.0;
double gWave = 560.0;
double bWave = 490.0;
double minValue = 0.0;
double maxValue = 255.0;
};
explicit RasterRenderer(RasterDataProvider* provider);
// Render to an 8-bit RGB image. Returns empty image on failure.
QImage render(const Params& params);
private:
RasterDataProvider* m_provider;
// helper to map float buffer to 8-bit with min/max stretch
static void stretchTo8bit(const std::vector<float>& in, std::vector<unsigned char>& out, float minVal, float maxVal);
};