(C++) Qt5.15.12 + GDAL库 等高线生成示例代码

Qt + GDAL 等高线生成示例

以下是REC533计算的5*5原始结果:

json 复制代码
REC533 Version 16.1207A
TANGIER, Morocco [HR 4/4/.5 ] 500kW 57deg 18ut 11.850MHz JUN 100ssn
  5  5  Latitude Longitude   MUF  MODE  ANGL   DBU   S/N  FS/N  SNxx
  1  1  -90.0000 -180.0000 10.17   ---   3.0  -0.7  34.4 0.010  13.7
  2  1  -90.0000  -90.0000 10.17   ---   3.0  -0.7  34.4 0.010  13.7
  3  1  -90.0000    0.0000 10.17   ---   3.0  -0.7  34.4 0.010  13.7
  4  1  -90.0000   90.0000 10.17   ---   3.0  -0.7  34.4 0.010  13.7
  5  1  -90.0000  180.0000 10.17   ---   3.0  -0.7  34.4 0.010  13.7
  1  2  -45.0000 -180.0000 10.73   ---   3.0  -8.8  25.8 0.010   8.4
  2  2  -45.0000  -90.0000 34.99   ---   3.0 -44.8 -11.1 0.010 -25.2
  3  2  -45.0000    0.0000 21.74   ---   3.0  16.8  49.6 0.010  35.7
  4  2  -45.0000   90.0000  9.29   ---   3.0   3.8  38.9 0.010  21.4
  5  2  -45.0000  180.0000 10.73   ---   3.0  -8.8  25.8 0.010   8.4
  1  3    0.0000 -180.0000 22.68   ---   3.0 -41.2  -7.7 0.010 -23.5
  2  3    0.0000  -90.0000 28.70   ---   3.0 -36.1  -1.3 0.010 -15.6
  3  3    0.0000    0.0000 24.55   3F2  15.0  30.6  63.8 0.107  49.6
  4  3    0.0000   90.0000 25.86   ---   3.0  32.6  65.0 0.119  51.6
  5  3    0.0000  180.0000 22.68   ---   3.0 -41.2  -7.7 0.010 -23.5
  1  4   45.0000 -180.0000 19.18   ---   3.0 -16.7  18.0 0.010   2.0
  2  4   45.0000  -90.0000 20.91   4F2  22.0   1.6  37.1 0.010  20.9
  3  4   45.0000    0.0000 13.53   1E    8.0  49.7  82.6 0.791  65.8
  4  4   45.0000   90.0000 23.08   -I-   3.0  51.4  86.1 0.891  72.3
  5  4   45.0000  180.0000 19.18   ---   3.0 -16.7  18.0 0.010   2.0
  1  5   90.0000 -180.0000 18.79   3F2  17.0  19.8  54.9 0.010  38.8
  2  5   90.0000  -90.0000 18.79   3F2  17.0  19.8  54.9 0.010  38.8
  3  5   90.0000    0.0000 18.79   3F2  17.0  19.8  54.9 0.010  38.8
  4  5   90.0000   90.0000 18.79   3F2  17.0  19.8  54.9 0.010  38.8
  5  5   90.0000  180.0000 18.79   3F2  17.0  19.8  54.9 0.010  38.8

REC533软件输出的图形:

基于Qt5.15.12 + GDAL计算的结果:

1️⃣ 功能概览

这份代码的主要功能是:

  1. 对原始栅格数据进行双线性插值,将低分辨率网格(如 5x5)映射到高分辨率网格(如 360x180)。
  2. 使用 GDAL 的等高线生成器(Contour Generator)生成等值线。
  3. 将生成的等高线绘制到 QImage 上,通过 QPainter 可视化。
  4. 支持将插值后的数据存储为内存栅格数据集(MEM)或导出为 ESRI Shapefile 矢量文件。
  5. 提供坐标系转换工具函数,将绘图坐标转换为地图坐标。
  6. 提供多边形数据管理工具,如按等高线等级分组存储多边形。

2️⃣ 详细描述

  1. 栅格插值

    • 从小网格(如 5×5)插值生成大网格(如 360×180)。
    • 使用 双线性插值 算法 (bilinearInterpolation / interpolateGrid)。
    • 支持无效值处理(NaN 或指定 NoData 值)。
  2. 等高线生成

    • 使用 GDAL 内存等高线生成器 (GDALContourGeneratorH)。
    • 按设定的 等高线间隔 (g_dfContourInterval) 和 基准值 (g_dfContourBase) 生成等高线。
    • 自定义回调函数 rasterContourWriter2
      • 转为 Qt 多边形 (QPolygonF)。
      • 使用 QPainter 绘制等高线。
      • 管理多条等高线等级 (g_vPolygonList)。
  3. 绘图与可视化

    • 生成 QImage / QPixmap,绘制红色等高线。
    • 可直接在 Qt 界面显示。
  4. 生成 GIS 数据

    • 内存栅格数据集 (MEM 驱动)。
    • 写入 GDAL 栅格波段。
    • 生成 Shapefile 矢量数据:
      • 每条等高线存为线要素。
      • 自动创建字段(ID、高程等)。
    • 使用 GDALContourGenerate 输出等高线。
  5. 坐标系转换

    • 从绘图坐标系 (0~360, 0~180) 转到地理坐标系 (-180~180, -90~90)。

GDAL-Test.pro

cpp 复制代码
QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17
DESTDIR = bin

# https://gisinternals.com/query.html?content=filelist&file=release-1929-x64-gdal-3-12-1-mapserver-8-6-0.zip
#       https://download.gisinternals.com/sdk/downloads/release-1929-x64-gdal-3-12-1-mapserver-8-6-0.zip
#       https://download.gisinternals.com/sdk/downloads/release-1929-x64-gdal-3-12-1-mapserver-8-6-0-libs.zip
INCLUDEPATH += "D:/class/gdal-3-12-1/include"
LIBS += -LD:/class/gdal-3-12-1/lib
LIBS += -lgdal_i

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    gdal_public.cpp \
    main.cpp \
    widget.cpp

HEADERS += \
    gdal_public.h \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

gdal_public.h

cpp 复制代码
#ifndef GDAL_PUBLIC_H
#define GDAL_PUBLIC_H

#include <QPainter>
#include <QImage>
#include <QPolygonF>
#include <QPointF>
#include <QVector>
#include <QDebug>

#include "cpl_conv.h"       // 必须放最前
#include "gdal_priv.h"
#include "gdal_alg.h"       // Contour API
#include "ogr_api.h"
#include "ogrsf_frmts.h"

#define SIZE_LAT   90
#define SIZE_LNG   180

typedef struct tagMY_POLYGON_LIST
{
    double              m_fLevel;
    QVector<QPolygonF>  m_vPolygon;
    tagMY_POLYGON_LIST() : m_fLevel(0.0f)  {}
    tagMY_POLYGON_LIST(double level) : m_fLevel(level) {}
}MY_POLYGON_LIST;

// 等高线的间隔和基准
extern double                      g_dfContourInterval;
extern double                      g_dfContourBase;
extern int                         g_bNoDataSet;
extern double                      g_dfNoDataValue;

extern QPixmap                     *g_PixmapResult;

extern QVector<QColor>             g_vColorBand;
extern QVector<MY_POLYGON_LIST *>  g_vPolygonList;

void addPolygon(double dfLevel, QPolygonF polygon);
void getDrawXY(double ax, double ay, double &bx, double & by);

void gdal_genPolygon();

double idw_get_z(const double x, const double y);

// 利用IDW插值算法补齐空白点
int idw_make_points();

int grid_bilinearInterpolation();

#endif // GDAL_PUBLIC_H

gdal_public.cpp

cpp 复制代码
#include "gdal_public.h"    // 自定义封装

// 定义常量:地球半径 (单位:千米)
const double R = 6371.0;

// 等高线的间隔和基准
double                      g_dfContourInterval = 2.0f;         // 等值线间隔
double                      g_dfContourBase = 24.0f;            // 等值线基准值, 从多少开始
int                         g_bNoDataSet = FALSE;               // 是否使用 NoData 值
double                      g_dfNoDataValue = std::nan("-9999.0");// 或者设置为一个特定的值,如 -9999

// 定义常见颜色并将其存储在 QVector 中
QVector<QColor>             g_vColorBand;

// 当前等值多边形
QVector<MY_POLYGON_LIST *>  g_vPolygonList;

QPixmap                     *g_PixmapResult;

// 按z值升序排序
bool comparePolygon(MY_POLYGON_LIST a, MY_POLYGON_LIST b) {
    return a.m_fLevel > b.m_fLevel;
}

// 比较两个浮点值是否相等
bool compareDoubles(double a, double b, double epsilon = 1e-9) {
    return std::fabs(a - b) < epsilon;
}

// 添加一个地图坐标系中的多边形
void addPolygon(double dfLevel, QPolygonF polygon)
{
    MY_POLYGON_LIST *pItem;

    foreach(pItem, g_vPolygonList)
    {
        if(compareDoubles(pItem->m_fLevel, dfLevel))
        {
            pItem->m_vPolygon.push_back(polygon);
            return;
        }
    }

    pItem = new MY_POLYGON_LIST;
    pItem->m_fLevel = dfLevel;
    pItem->m_vPolygon.push_back(polygon);
    g_vPolygonList.push_back(pItem);
}

// 从绘图坐标获取地图坐标的位置
//坐标系A:矩形左上角X=0,Y=0;        左下角X=0,Y=180;       右上角X=360,Y=0;       下角X=360,Y=180
//坐标系B:矩形左上角X=-180,Y=90;    左下角X=-180,Y=-90;    右上角X=180,Y=90;      右下角X=180,Y=-90
void getDrawXY(double ax, double ay, double &bx, double & by)
{
    // X轴转换:坐标系A的 [0, 360] 转换到坐标系B的 [-180, 180]
    bx = ax - 180.0f;

    // Y轴转换:坐标系A的 [0, 180] 转换到坐标系B的 [-90, 90]
    by = 90.0f - ay;
}


// 双线性插值求未知点坐标
double bilinearInterpolation(double x, double y, const std::vector<std::vector<double>>& grid)
{
    int x1 = static_cast<int>(x);
    int y1 = static_cast<int>(y);
    int x2 = x1 + 1;
    int y2 = y1 + 1;

    // 防止越界
    x2 = qMin(x2, static_cast<int>(grid.size()) - 1);
    y2 = qMin(y2, static_cast<int>(grid[0].size()) - 1);

    double q11 = grid[x1][y1];
    double q12 = grid[x1][y2];
    double q21 = grid[x2][y1];
    double q22 = grid[x2][y2];

    double denom = (x2 - x1) * (y2 - y1);

    // 双线性插值公式
    double interpolatedValue = q11 * (x2 - x) * (y2 - y) / denom
                               + q21 * (x - x1) * (y2 - y) / denom
                               + q12 * (x2 - x) * (y - y1) / denom
                               + q22 * (x - x1) * (y - y1) / denom;

    // 过滤无效值
    if(isnan(interpolatedValue))
        return -9999.0f;

    return interpolatedValue;
}

// 双线性插值算法: 将30x30网格映射到360x180网格
std::vector<std::vector<double>> interpolateGrid(const std::vector<std::vector<double>> & originalGrid, int targetWidth, int targetHeight)
{
    int                                 originalWidth = originalGrid.size();
    int                                 originalHeight = originalGrid[0].size();
    std::vector<std::vector<double>>    targetGrid(targetWidth, std::vector<double>(targetHeight));

    for (int j = 0; j < targetHeight; ++j)
    {
        for (int i = 0; i < targetWidth; ++i)
        {
            // 计算原始网格坐标
            double origX = static_cast<double>(i) / (targetWidth - 1) * (originalWidth - 1);
            double origY = static_cast<double>(j) / (targetHeight - 1) * (originalHeight - 1);

            // 使用双线性插值计算目标网格点的值
            targetGrid[i][j] = bilinearInterpolation(origX, origY, originalGrid);
        }
    }

    return targetGrid;
}


// 自定义的等高线写操作器
CPLErr rasterContourWriter2(double dfLevel, int nPoints, double *padfX, double *padfY, void *ptr)
{
    char        szMsg[256] = {0x00};

    // 小于基数的Z值不处理
    if(dfLevel < g_dfContourBase)
        return CE_None;

    // 如果需要绘制,可以使用 QPainter
    QPainter    *painter = static_cast<QPainter*>(ptr);  // 获取 QPainter 对象
    QPolygonF   polygon, polyDraw;
    double      bx, by;
    for (int i = 0; i < nPoints; i++) {
        polygon.append(QPointF(padfX[i], padfY[i]));

        getDrawXY(padfX[i], padfY[i], bx, by);
        polyDraw.append(QPointF(bx, by));
        sprintf(szMsg, "   level:%.2f, ax=%.2f, ay=%.2f, bx=%.2f, by=%.2f", dfLevel, padfX[i], padfY[i], bx, by);
        qDebug() << szMsg;

    }
    addPolygon(dfLevel, polyDraw);

    // 如果该多边形红框无效或面积小于10
    if(!polygon.isClosed() || polygon.isEmpty())
        return CE_None;

    // 创建一个红色的画笔,并设置线宽为1
    QPen pen;
    pen.setColor(Qt::red);
    pen.setWidth(1);

    // 设置画笔并绘制多边形
    painter->setPen(pen);
    painter->setBrush(Qt::NoBrush);  // 不填充

    // 设置边框色
    QPen borderPen(QColor(0, 0, 0));  // 黑色边框
    painter->setPen(borderPen);
    painter->drawPolygon(polygon);
    return CE_None;
}



// 双线性插值方法
int grid_bilinearInterpolation()
{
    // 设置目标网格大小为360x180
    const int                   targetWidth = 360;
    const int                   targetHeight = 180;

    // 它是GDAL库中的一个等高线生成器类
    GDALContourGeneratorH       cg = NULL;

    // 一定要初始化 GDAL
    GDALAllRegister();

    // 存储插值后的数据点阵(左上角为0,0;右下角为正值区)
    std::vector<std::vector<double>>    targetGrid;

    // 按行存储所有插值结果(左上角为0,0;右下角为正值区;从上至下和从左至右扫描)
    double                              *pZValues = NULL, *pScanLine = NULL;

    // 假设原始网格数据是5x5的
    const int       originalSize = 5;
    std::vector<std::vector<double>> originalGrid(originalSize, std::vector<double>(originalSize));

    // Latitude:    纬度Y
    // Longitude:   经度X
    // [X][Y]

    /*
  5  5  Latitude Longitude   MUF  MODE  ANGL   DBU   S/N  FS/N  SNxx
  1  1  -90.0000 -180.0000 10.17   ---   3.0  -0.7  34.4 0.010  13.7
  2  1  -90.0000  -90.0000 10.17   ---   3.0  -0.7  34.4 0.010  13.7
  3  1  -90.0000    0.0000 10.17   ---   3.0  -0.7  34.4 0.010  13.7
  4  1  -90.0000   90.0000 10.17   ---   3.0  -0.7  34.4 0.010  13.7
  5  1  -90.0000  180.0000 10.17   ---   3.0  -0.7  34.4 0.010  13.7
*/
    originalGrid[0][4] = 10.17;
    originalGrid[1][4] = 10.17;
    originalGrid[2][4] = 10.17;
    originalGrid[3][4] = 10.17;
    originalGrid[4][4] = 10.17;

    /*
  1  2  -45.0000 -180.0000 10.73   ---   3.0  -8.8  25.8 0.010   8.4
  2  2  -45.0000  -90.0000 34.99   ---   3.0 -44.8 -11.1 0.010 -25.2
  3  2  -45.0000    0.0000 21.74   ---   3.0  16.8  49.6 0.010  35.7
  4  2  -45.0000   90.0000  9.29   ---   3.0   3.8  38.9 0.010  21.4
  5  2  -45.0000  180.0000 10.73   ---   3.0  -8.8  25.8 0.010   8.4
*/
    originalGrid[0][3] = 10.73;
    originalGrid[1][3] = 34.99;
    originalGrid[2][3] = 21.74;
    originalGrid[3][3] = 9.29;
    originalGrid[4][3] = 10.73;

    /*
  1  3    0.0000 -180.0000 22.68   ---   3.0 -41.2  -7.7 0.010 -23.5
  2  3    0.0000  -90.0000 28.70   ---   3.0 -36.1  -1.3 0.010 -15.6
  3  3    0.0000    0.0000 24.55   3F2  15.0  30.6  63.8 0.107  49.6
  4  3    0.0000   90.0000 25.86   ---   3.0  32.6  65.0 0.119  51.6
  5  3    0.0000  180.0000 22.68   ---   3.0 -41.2  -7.7 0.010 -23.5
*/
    originalGrid[0][2] = 22.68;
    originalGrid[1][2] = 28.70;
    originalGrid[2][2] = 24.55;
    originalGrid[3][2] = 25.86;
    originalGrid[4][2] = 22.68;

    /*
  1  4   45.0000 -180.0000 19.18   ---   3.0 -16.7  18.0 0.010   2.0
  2  4   45.0000  -90.0000 20.91   4F2  22.0   1.6  37.1 0.010  20.9
  3  4   45.0000    0.0000 13.53   1E    8.0  49.7  82.6 0.791  65.8
  4  4   45.0000   90.0000 23.08   -I-   3.0  51.4  86.1 0.891  72.3
  5  4   45.0000  180.0000 19.18   ---   3.0 -16.7  18.0 0.010   2.0
*/
    originalGrid[0][1] = 19.18;
    originalGrid[1][1] = 20.91;
    originalGrid[2][1] = 13.53;
    originalGrid[3][1] = 23.08;
    originalGrid[4][1] = 19.18;

    /*
    1  5   90.0000 -180.0000 18.79   3F2  17.0  19.8  54.9 0.010  38.8
    2  5   90.0000  -90.0000 18.79   3F2  17.0  19.8  54.9 0.010  38.8
    3  5   90.0000    0.0000 18.79   3F2  17.0  19.8  54.9 0.010  38.8
    4  5   90.0000   90.0000 18.79   3F2  17.0  19.8  54.9 0.010  38.8
    5  5   90.0000  180.0000 18.79   3F2  17.0  19.8  54.9 0.010  38.8
*/
    originalGrid[0][0] = 18.79;
    originalGrid[1][0] = 18.79;
    originalGrid[2][0] = 18.79;
    originalGrid[3][0] = 18.79;
    originalGrid[4][0] = 18.79;

    /////////////////////////////////////////////////////////////////////////////////
    // 插值并获得目标网格数据
    targetGrid = interpolateGrid(originalGrid, targetWidth, targetHeight);

    // 输出插值后的结果(这里只打印一些值来验证)
    pZValues = new double[targetWidth * targetHeight * sizeof(double)];
    for (int i = 0; i < targetHeight; ++i)
    {
        for (int j = 0; j < targetWidth; ++j)
        {
            pZValues[i * targetWidth + j] = targetGrid[j][i];
        }
    }

    // 这里是调用QImage存储结果(可忽略)
    // 创建一个简单的 QPainter 用于绘制等高线
    QImage      *image = new QImage(targetWidth, targetHeight, QImage::Format_ARGB32);
    QPainter    *painter = new QPainter(image);
    painter->setPen(Qt::black);
    painter->setRenderHint(QPainter::Antialiasing); // 启用抗锯齿

    cg = GDAL_CG_Create(targetWidth, targetHeight, g_bNoDataSet, g_dfNoDataValue, g_dfContourInterval, g_dfContourBase, rasterContourWriter2, painter);

    // 将内存数据传递给等高线生成器
    pScanLine = pZValues;
    for (int i = 0; i < targetHeight; ++i) {
        GDAL_CG_FeedLine(cg, pScanLine);
        pScanLine += targetWidth;  // 每行宽度
    }
    GDAL_CG_Destroy(cg);


    // 保存生成的图像
    image->save("./123.png");
    if(!g_PixmapResult)
        g_PixmapResult = new QPixmap();
    *g_PixmapResult = QPixmap::fromImage(*image);

    /////////////////////////////////////////////////////////////////////////////////////////////////////
    // 生成tif创建内存中的栅格数据集
    double xMin = -targetWidth/2;
    double xMax = targetWidth/2;
    double yMin = -targetHeight/2;
    double yMax = targetHeight/2;

    // 栅格行列数 (1 度 × 1 度分辨率)
    int     rows = targetHeight;
    int     cols = targetWidth;

    GDALDriver* poDriver = GetGDALDriverManager()->GetDriverByName("MEM");
    if (!poDriver) {
        return 0;
    }

    GDALDataset* poDataset = poDriver->Create("contours.shp", cols, rows, 1, GDT_Float64, nullptr);
    if (!poDataset) {
        return 0;
    }

    // 设置地理变换(左上角坐标和像素大小)
    double pixelSizeX = (double)(xMax - xMin) / (double)cols;
    double pixelSizeY = (double)(yMax - yMin) / (double)rows;
    double adfGeoTransform[6] = {(double)xMin, pixelSizeX, 0, (double)yMax, 0, -pixelSizeY}; // y 轴向下为正
    poDataset->SetGeoTransform(adfGeoTransform);

    // 写入插值后的 z 值数据
    GDALRasterBand          *poBand = poDataset->GetRasterBand(1);

    poBand->RasterIO(GF_Write, 0, 0, cols, rows, pZValues, cols, rows, GDT_Float64, 0, 0);

    // 创建矢量数据集(用于存储等值线)
    GDALDriver* poVectorDriver = GetGDALDriverManager()->GetDriverByName("ESRI Shapefile");
    if (!poVectorDriver) {
        return 0;
    }

    GDALDataset* poVectorDataset = poVectorDriver->Create("contours.shp", 0, 0, 0, GDT_Unknown, nullptr);
    if (!poVectorDataset) {
        return 0;
    }

    // 创建图层
    OGRLayer* poLayer = poVectorDataset->CreateLayer("contours", nullptr, wkbLineString, nullptr);
    if (!poLayer) {
        return 0;
    }

    // 添加字段(等值线值)
    OGRFieldDefn oField("ID", OFTInteger);
    if (poLayer->CreateField(&oField) != OGRERR_NONE) {
        return 0;
    }

    // 生成等值线
    double  contourInterval = 2.0;          // 等值线间隔
    double  contourBase = 22.0;             // 等值线基准值
    int     nFixedLevelCount = 0;           // 固定等值线数量
    double  *fixedLevels = nullptr;         // 固定等值线值
    int     bUseNoData = FALSE;             // 是否使用 NoData 值
    double  dfNoDataValue = std::nan("");   // NoData 值,使用 NaN
    int     iIDField = 0;                   // 字段索引
    int     iElevField = 0;                 // 高程字段索引

    CPLErr err = GDALContourGenerate(
        poBand, contourInterval, contourBase, nFixedLevelCount, fixedLevels,
        bUseNoData, dfNoDataValue, poLayer, iIDField, iElevField, nullptr, nullptr
        );
    if (err != CE_None) {
        return 0;
    }

    // 清理
    GDALClose(poDataset);
    GDALClose(poVectorDataset);

    delete [] pZValues;
    pZValues = NULL;
    return 1;
}

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPainter>
#include <QPaintEvent>
#include <QMessageBox>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    int     getNewPoint(int iOldX, int iOldY, int &iNewX, int &iNewY);
    int     getOldPoint(int iNewX, int iNewY, int &iOldX, int &iOldY);

    void    drawBack(QPainter & painter);
    void    drawPolygonList(QPainter & painter);

protected:
    void mousePressEvent(QMouseEvent *event) override;
    void paintEvent(QPaintEvent *event) override;


public:
    double m_fScaleX, m_fScaleY;

private:
    double xMin = -180.0, xMax = 180.0;
    double yMin = -90.0, yMax = 90.0;

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include "gdal_public.h"


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

    this->resize(360 * 2, 180 * 2);

    grid_bilinearInterpolation();

    ui->label->setPixmap(*g_PixmapResult);
    ui->label->setScaledContents(true); // 直接拉伸内容
    ui->label->setAlignment(Qt::AlignCenter);
}

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


int Widget::getNewPoint(int iOldX, int iOldY, int &iNewX, int &iNewY)
{
    // 获取窗口的宽度和高度
    int width = this->width();
    int height = this->height();

    // 坐标系的范围
    //double xMin = -181.0, xMax = 181.0;
    //double yMin = -91.0, yMax = 91.0;

    // 计算缩放因子
    double scaleX = (xMax - xMin) / width;
    double scaleY = (yMax - yMin) / height;

    // 计算坐标平移后的原始坐标
    double translatedX = iOldX - (width / 2);
    double translatedY = iOldY - (height / 2);

    // 将原始坐标转换为目标坐标系
    iNewX = translatedX * scaleX;  // 转换为 x 坐标
    iNewY = -translatedY * scaleY; // 转换为 y 坐标(y轴翻转)

    return 0;
}


int Widget::getOldPoint(int iNewX, int iNewY, int &iOldX, int &iOldY)
{
    // 获取窗口的宽度和高度
    int width = this->width();
    int height = this->height();

    // 坐标系的范围
    //double xMin = -180.0, xMax = 180.0;
    //double yMin = -90.0, yMax = 90.0;

    // 计算缩放因子
    double scaleX = (xMax - xMin) / width;
    double scaleY = (yMax - yMin) / height;

    // 反向缩放,得到窗口坐标系的值
    double translatedX = (iNewX / scaleX);
    double translatedY = (iNewY / scaleY);

    // 反向平移,使得坐标回到左上角为原点的坐标系
    iOldX = translatedX + (width / 2);
    iOldY = translatedY + (height / 2);

    return 0;
}


void Widget::drawBack(QPainter & painter)
{
    // 获取窗口的宽度和高度
    int width = this->width();
    int height = this->height();

    // 坐标系的范围,窗口区域为 361 x 181
    double xMin = 0.0, xMax = 360.0;
    double yMin = 0.0, yMax = 180.0;

    // 计算缩放因子,确保坐标系统在窗口拉伸时自适应
    double scaleX = width / (xMax - xMin);
    double scaleY = height / (yMax - yMin);

    // 设置缩放因子
    painter.scale(scaleX, scaleY);

    // 将坐标系平移到窗口的左上角 (0, 0)
    painter.translate(xMin, yMin);

    // 绘制一个矩形,左上角为 (0, 0),右下角为 (361, 181)
    painter.setBrush(QBrush(QColor(200, 200, 200)));
    painter.drawRect(0, 0, 360, 180);  // 绘制一个矩形

    // 绘制坐标轴
    painter.setPen(Qt::black);
    painter.drawLine(0, 0, 360, 0); // x 轴
    painter.drawLine(0, 0, 0, 180); // y 轴
    \
        // 注意一下,虽然旋转调整了Y轴,但绘图函数仍是以上角为中心点
        QPolygonF polygon;
    polygon.append(QPointF(10, 10));
    polygon.append(QPointF(350, 10));
    polygon.append(QPointF(350, 170));
    polygon.append(QPointF(10, 170));
    polygon.append(QPointF(10, 10));
    painter.drawPolygon(polygon);

    // 绘制网格线
    painter.setPen(QPen(Qt::blue, 0.1));
    for (int i = 0; i <= 360; i += 30) {
        painter.drawLine(i, 0, i, 181);  // 绘制竖直线
    }
    for (int j = 0; j <= 180; j += 30) {
        painter.drawLine(0, j, 360, j);  // 绘制水平线
    }
}

void Widget::mousePressEvent(QMouseEvent *event)
{
    int x, y, iOldX, iOldY;
    getNewPoint(event->x(), event->y(), x, y);

    getOldPoint(x, y, iOldX, iOldY);

    int     width = this->width();
    int     height = this->height();
    double scaleX = (xMax - xMin) / width;
    double scaleY = (yMax - yMin) / height;

    iOldX *= scaleX;
    iOldY *= scaleY;

    QString     strText = QString("iNewX=%1, iNewY=%2, iOldX=%3, iOldY=%3").arg(x).arg(y).arg(iOldX).arg(iOldY);
    setWindowTitle(strText);
}

void Widget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)

    QPainter    painter(this);

    drawBack(painter);
    //drawPolygonList(painter);
}

void Widget::drawPolygonList(QPainter & painter)
{
    int     index = 0;

    // 添加常见颜色到 colorBand
    if(g_vColorBand.size() == 0)
    {
        g_vColorBand.append(QColor(255, 0, 0));    // Red
        g_vColorBand.append(QColor(0, 255, 0));    // Green
        g_vColorBand.append(QColor(0, 0, 255));    // Blue
        g_vColorBand.append(QColor(255, 255, 0));  // Yellow
        g_vColorBand.append(QColor(255, 165, 0));  // Orange
        g_vColorBand.append(QColor(75, 0, 130));   // Indigo
        g_vColorBand.append(QColor(238, 130, 238)); // Violet
        g_vColorBand.append(QColor(0, 255, 255));  // Cyan
        g_vColorBand.append(QColor(255, 255, 255)); // White
        g_vColorBand.append(QColor(0, 0, 0));      // Black
    }

    if(g_vPolygonList.size() == 0)
        return;

    foreach(MY_POLYGON_LIST *pList, g_vPolygonList)
    {
        // 设置画笔颜色(边框颜色)
        QPen pen(Qt::black);  // 黑色边框
        painter.setPen(pen);

        // 设置填充颜色
        QBrush brush(g_vColorBand.at(index));  // 填充蓝色
        painter.setBrush(brush);

        foreach(QPolygonF polygon, pList->m_vPolygon)
        {
            painter.drawPolygon(polygon);
        }

        index++;
    }
}

widget.ui

cpp 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Widget</class>
 <widget class="QWidget" name="Widget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Widget</string>
  </property>
  <layout class="QGridLayout" name="gridLayout">
   <item row="0" column="0">
    <widget class="QLabel" name="label">
     <property name="text">
      <string/>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

main.cpp

cpp 复制代码
#include "widget.h"

#include <QApplication>
#include <QCoreApplication>
#include <QDir>
#include <QDebug>
#include <QProcessEnvironment>

#ifdef Q_OS_WIN
#include <windows.h>
#endif

void addGdalBinToPath()
{
    // 1. 获取可执行文件所在目录
    QString exeDir = QCoreApplication::applicationDirPath();

    // 2. 拼接 GDAL bin 路径
    QString gdalBinPath = QDir(exeDir).filePath("gdal_bin");

#ifdef Q_OS_WIN
    // 3. 获取当前 PATH
    wchar_t buffer[32767]; // 最大环境变量长度
    DWORD len = GetEnvironmentVariableW(L"PATH", buffer, 32767);
    QString currentPath = QString::fromWCharArray(buffer, len);

    // 4. 将 gdal_bin 添加到 PATH 前面
    QString newPath = gdalBinPath + ";" + currentPath;

    // 5. 设置当前进程的环境变量
    SetEnvironmentVariableW(L"PATH", (const wchar_t*)newPath.utf16());
#else
    // Linux / macOS
    QByteArray pathEnv = qgetenv("PATH");
    pathEnv = gdalBinPath.toUtf8() + ":" + pathEnv;
    qputenv("PATH", pathEnv);
#endif

    qDebug() << "GDAL bin path added to PATH:" << gdalBinPath;
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}
相关推荐
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 155. 最小栈 | C++ 打包状态法 (最优雅的 O(1) 检索)
java·c++·leetcode
Kurisu_红莉栖2 小时前
c++的复习——多态
开发语言·c++
草莓熊Lotso2 小时前
手搓工业级 C++ 线程安全日志系统:基于策略模式解耦,兼容 glog 使用风格
linux·运维·服务器·数据库·c++·安全·策略模式
pearlthriving2 小时前
STL容器及其底层
开发语言·c++·算法
我在人间贩卖青春2 小时前
Qt 元对象系统(MOC)
qt·moc·元对象
Byte不洛2 小时前
深入理解C++多态机制:虚函数、虚表与对象内存模型解析
c++·多态·对象模型·虚函数表·虚基表
leaves falling2 小时前
C++ 继承详解:从入门到深入
开发语言·c++
minji...2 小时前
Linux 网络基础(一)认识协议,网络协议,网络协议分层框架搭建,网络传输基本流程,跨网络的数据传输
linux·运维·服务器·网络·c++·网络协议
吃着火锅x唱着歌2 小时前
深度探索C++对象模型 学习笔记 第四章 Function语意学(1)
c++·笔记·学习