Program Listing for File dem.cc

Return to documentation for file (data/dem.cc)

#include "lupnt/data/dem.h"

#include <cmath>
#include <vector>

#include "lupnt/core/error.h"

namespace lupnt {
  std::tuple<MatXd, MatXd, MatXd> LoadTiff(std::filesystem::path path, std::array<double, 2> xlims,
                                           std::array<double, 2> ylims, double max_res) {
    GDALAllRegister();
    GDALDataset* dataset = (GDALDataset*)GDALOpen(path.string().c_str(), GA_ReadOnly);
    LUPNT_CHECK(dataset, "Failed to open TIFF", "LoadTiff");

    double gt[6];
    dataset->GetGeoTransform(gt);
    LUPNT_CHECK(gt, "Failed to get GeoTransform", "LoadTiff");

    int width_src = dataset->GetRasterXSize();
    int height_src = dataset->GetRasterYSize();
    double pixel_size_x = gt[1];
    double pixel_size_y = std::abs(gt[5]);

    double x_src_min = gt[0];
    double y_src_max = gt[3];
    double x_src_max = x_src_min + width_src * pixel_size_x;
    double y_src_min = y_src_max - height_src * pixel_size_y;

    double x_min = std::max(x_src_min, xlims[0]);
    double x_max = std::min(x_src_max, xlims[1]);
    double y_min = std::max(y_src_min, ylims[0]);
    double y_max = std::min(y_src_max, ylims[1]);

    int col_min = std::max(0, static_cast<int>(std::floor((x_min - gt[0]) / pixel_size_x)));
    int col_max = std::min(width_src, static_cast<int>(std::ceil((x_max - gt[0]) / pixel_size_x)));
    int row_min = std::max(0, static_cast<int>(std::floor((gt[3] - y_max) / pixel_size_y)));
    int row_max = std::min(height_src, static_cast<int>(std::ceil((gt[3] - y_min) / pixel_size_y)));

    int width = col_max - col_min;
    int height = row_max - row_min;

    int ds_x = std::max(1, static_cast<int>(std::ceil(max_res / pixel_size_x)));
    int ds_y = std::max(1, static_cast<int>(std::ceil(max_res / pixel_size_y)));

    int out_width = width / ds_x;
    int out_height = height / ds_y;

    std::vector<float> buffer(out_width * out_height);
    GDALRasterBand* band = dataset->GetRasterBand(1);

    GDALRasterIOExtraArg args;
    INIT_RASTERIO_EXTRA_ARG(args);
    args.eResampleAlg = GRIORA_NearestNeighbour;

    CPLErr err = band->RasterIO(GF_Read, col_min, row_min, width, height, buffer.data(), out_width,
                                out_height, GDT_Float32, 0, 0, &args);
    if (err != CE_None) LUPNT_CHECK(false, "Failed to read raster data", "LoadTiff");

    // Create Eigen matrices
    MatXd data(out_height, out_width);
    MatXd x_coords(out_height, out_width);
    MatXd y_coords(out_height, out_width);

    double origin_x = gt[0] + col_min * pixel_size_x;
    double origin_y = gt[3] - row_min * pixel_size_y;

    // Fill the matrices
    for (int r = 0; r < out_height; ++r) {
      for (int c = 0; c < out_width; ++c) {
        data(r, c) = static_cast<double>(buffer[r * out_width + c]);
        x_coords(r, c) = origin_x + (c + 0.5) * ds_x * pixel_size_x;
        y_coords(r, c) = origin_y - (r + 0.5) * ds_y * pixel_size_y;
      }
    }

    GDALClose(dataset);
    return {x_coords, y_coords, data};
  }
}  // namespace lupnt