Problem: C++ Simplified Raster Processing Functions

The problem requires creating simplified C++ functions for raster data operations—including reading basic info, geotransform, projection, subsets, saving rasters, and computing eight statistics—with file existence checks, serving as a foundational framework for GIS data processing before full GDAL integration.


Input/Output: The problem receives inputs including raster file paths (std::string), optional double arrays for geotransform parameters, projection strings (std::string), 2D std::vector<double> raster data, integer offsets (xoff, yoff), sizes (xsize, ysize), and band numbers (int). It generates outputs: a std::vector<int> with raster rows, columns, and bands from readRasterInfo; a bool indicating geotransform read success (and modified double array) from readGeoTransform; a std::string projection from readProjection; a 2D std::vector<double> raster subset from readRasterSubset; a bool for save success from saveRaster; and a std::vector<double> with min, max, mean, median, 25th/75th percentiles, variance, and standard deviation from calculateRasterStats. Processing involves file existence checks via _access, simplified raster I/O (full GDAL required for actual handling), and statistical computations (value collection, sorting, linear interpolation for percentiles).

Required dependencies:
#include <iostream>
#include <vector>
#include <string>
#include <io.h>
#include <algorithm>
#include <cmath>
#include <limits>
using namespace std;

Sub-steps:
step_number: 1
step_description_prompt: Write a C++ function named readRasterInfo that takes a const std::string& filepath as parameter and returns a std::vector<int>, which implements reading raster basic information (simplified version). The function definition is std::vector<int> readRasterInfo(const std::string& filepath). Algorithm flow: 1. Define a RasterInfo struct with int members nRows, nCols, nBands (all initialized to 0 in constructor). 2. Define a lambda fileExists (takes const std::string&, returns bool) that checks file existence using _access from <io.h>. 3. Create a RasterInfo instance info with default values. 4. Use fileExists to check the filepath: if not exists, output "Warning: Raster file not found: " followed by filepath, then return a vector containing info.nRows, info.nCols, info.nBands; if exists, (simplified version only checks existence, full implementation requires GDAL library) return the same vector. Functions used (like _access) are assumed to be defined. The function checks if the raster file exists during execution.
function_header: std::vector<int> readRasterInfo(const std::string& filepath)
test_cases: int main() {
    // Get relative path (from problems directory to data directory)
    auto getDataPath = [](const std::string& relativePath) -> std::string {
        return "../test_data/gisdata/" + relativePath;
    };
    
    std::cout << "Subproblem 5.1: Read raster basic information" << std::endl;
    std::cout << "==============================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: Check if function returns {0, 0, 0} (simplified version)
    std::string rasterPath1 = getDataPath("landsat/20180523.img");
    auto result1 = readRasterInfo(rasterPath1);
    std::vector<int> expected1 = {0, 0, 0};  // Expected: {0, 0, 0}
    bool passed1 = (result1.size() == expected1.size() && result1 == expected1);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (passed1) correctCount++;
    
    // Test case 2: Check if function returns {0, 0, 0} for different path
    std::string rasterPath2 = getDataPath("mask/mask.shp");
    auto result2 = readRasterInfo(rasterPath2);
    std::vector<int> expected2 = {0, 0, 0};  // Expected: {0, 0, 0}
    bool passed2 = (result2.size() == expected2.size() && result2 == expected2);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (passed2) correctCount++;
    
    // Test case 3: Check if function returns {0, 0, 0} for another path
    std::string rasterPath3 = getDataPath("pm10_pts/pm10_pts.shp");
    auto result3 = readRasterInfo(rasterPath3);
    std::vector<int> expected3 = {0, 0, 0};  // Expected: {0, 0, 0}
    bool passed3 = (result3.size() == expected3.size() && result3 == expected3);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 5.1: Completely correct!" << std::endl;
    }
    
    return 0;
}

step_number: 2
step_description_prompt: Write a C++ function named readGeoTransform that checks if a file exists and (in this simplified version) returns false without reading geotransform parameters. The function has the signature bool readGeoTransform(const std::string& filepath, double* geotransform). The algorithm flow: 1. Define an internal lambda function fileExists which takes a const std::string& filepath and returns a bool by calling _access(filepath.c_str(), 0) == 0. 2. Use this lambda to check if the input file exists; if not, return false. 3. Return false (simplified version with no actual geotransform reading). Assume the lambda and _access are pre-defined. Include the file existence check using the lambda.
function_header: bool readGeoTransform(const std::string& filepath, double* geotransform)
test_cases: int main() {
    // Comparison function for vectors
    auto isEqualVec = [](const std::vector<int>& a, const std::vector<int>& b) {
        if (a.size() != b.size()) return false;
        for (size_t i = 0; i < a.size(); ++i) {
            if (a[i] != b[i]) return false;
        }
        return true;
    };
    
    // Get relative path (from problems directory to data directory)
    auto getDataPath = [](const std::string& relativePath) -> std::string {
        return "../test_data/gisdata/" + relativePath;
    };
    
    // Helper function to read raster info from text file
    auto readRasterInfoReal = [](const std::string& filepath) -> std::vector<int> {
        std::string txtPath;
        if (filepath.find("landsat/20180523.img") != std::string::npos) {
            txtPath = "../test_data/gisdata/landsat_raster_info.txt";
        } else if (filepath.find("demo.jpg") != std::string::npos) {
            txtPath = "../test_data/gisdata/demo_raster_info.txt";
        } else if (filepath.find("p1.jpg") != std::string::npos) {
            txtPath = "../test_data/gisdata/p1_raster_info.txt";
        } else {
            return {0, 0, 0};
        }
        
        std::vector<int> result = {0, 0, 0};
        std::ifstream file(txtPath);
        if (!file.is_open()) {
            return result;
        }
        
        std::string line;
        int index = 0;
        while (std::getline(file, line) && index < 3) {
            if (!line.empty()) {
                try {
                    result[index] = std::stoi(line);
                    index++;
                } catch (...) {
                    // Skip invalid lines
                }
            }
        }
        file.close();
        return result;
    };
    
    std::cout << "Subproblem 5.1: Read raster basic information" << std::endl;
    std::cout << "==============================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: landsat
    std::string rasterPath1 = getDataPath("landsat/20180523.img");
    auto result1 = readRasterInfoReal(rasterPath1);
    std::vector<int> expected1 = {1460, 2095, 11};
    bool passed1 = isEqualVec(result1, expected1);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (!passed1) {
        std::cout << "  Actual result: [" << result1[0] << ", " << result1[1] << ", " << result1[2] << "]" << std::endl;
    }
    if (passed1) correctCount++;
    
    // Test case 2: demo.jpg
    std::string rasterPath2 = getDataPath("demo.jpg");
    auto result2 = readRasterInfoReal(rasterPath2);
    std::vector<int> expected2 = {1846, 3295, 3};
    bool passed2 = isEqualVec(result2, expected2);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (!passed2) {
        std::cout << "  Actual result: [" << result2[0] << ", " << result2[1] << ", " << result2[2] << "]" << std::endl;
    }
    if (passed2) correctCount++;
    
    // Test case 3: p1.jpg
    std::string rasterPath3 = getDataPath("p1.jpg");
    auto result3 = readRasterInfoReal(rasterPath3);
    std::vector<int> expected3 = {435, 579, 3};
    bool passed3 = isEqualVec(result3, expected3);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (!passed3) {
        std::cout << "  Actual result: [" << result3[0] << ", " << result3[1] << ", " << result3[2] << "]" << std::endl;
    }
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 5.1: Completely correct!" << std::endl;
    }
    
    return 0;
}

step_number: 3
step_description_prompt: Please write a C++ function named readProjection that reads projection information from a file (simplified version). The function definition is std::string readProjection(const std::string& filepath). The algorithm flow is: use a lambda function fileExists (which checks file existence via _access) to verify if the input file exists; if the file does not exist, return an empty string; the simplified version directly returns an empty string (full implementation requires GDAL library). Called functions (fileExists lambda, _access) are already defined. The function involves checking file existence.
function_header: std::string readProjection(const std::string& filepath)
test_cases: int main() {
    // Get relative path (from problems directory to data directory)
    auto getDataPath = [](const std::string& relativePath) -> std::string {
        return "../test_data/gisdata/" + relativePath;
    };
    
    // Helper function to read geotransform from text file
    auto readGeoTransformReal = [](const std::string& filepath, double* geotransform) -> bool {
        if (geotransform == NULL) {
            return false;
        }
        
        std::string txtPath;
        if (filepath.find("landsat/20180523.img") != std::string::npos) {
            txtPath = "../test_data/gisdata/landsat_geotransform.txt";
        } else if (filepath.find("demo.jpg") != std::string::npos) {
            txtPath = "../test_data/gisdata/demo_geotransform.txt";
        } else if (filepath.find("p1.jpg") != std::string::npos) {
            txtPath = "../test_data/gisdata/p1_geotransform.txt";
        } else if (filepath.find("p2.png") != std::string::npos) {
            txtPath = "../test_data/gisdata/p2_geotransform.txt";
        } else {
            return false;
        }
        
        std::ifstream file(txtPath);
        if (!file.is_open()) {
            return false;
        }
        
        std::string line;
        int index = 0;
        while (std::getline(file, line) && index < 6) {
            if (!line.empty()) {
                try {
                    geotransform[index] = std::stod(line);
                    index++;
                } catch (...) {
                    // Skip invalid lines
                }
            }
        }
        file.close();
        
        return (index == 6);
    };
    
    std::cout << "Subproblem 5.2: Read geotransform parameters" << std::endl;
    std::cout << "===========================================" << std::endl << std::endl;
    
    // Comparison function for geotransform arrays
    auto isEqualGT = [](const double* a, const double* b, double tolerance = 1e-6) {
        for (int i = 0; i < 6; ++i) {
            if (std::abs(a[i] - b[i]) > tolerance) return false;
        }
        return true;
    };
    
    int correctCount = 0;
    
    // Test case 1: landsat
    std::string rasterPath1 = getDataPath("landsat/20180523.img");
    double gt1[6] = {0};
    bool result1 = readGeoTransformReal(rasterPath1, gt1);
    double expected1_gt[6] = {353895.0, 30.0, 0.0, 3477765.0, 0.0, -30.0};
    bool expected1_success = true;
    bool passed1 = (result1 == expected1_success) && isEqualGT(gt1, expected1_gt, 0.01);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (!passed1) {
        std::cout << "  Actual success: " << (result1 ? "true" : "false") << std::endl;
        std::cout << "  Actual geotransform: ";
        std::cout << std::fixed << std::setprecision(1);
        std::cout << "[" << gt1[0] << ", " << gt1[1] << ", " << gt1[2] << ", " 
                  << gt1[3] << ", " << gt1[4] << ", " << gt1[5] << "]" << std::endl;
        std::cout.unsetf(std::ios_base::fixed);
    }
    if (passed1) correctCount++;
    
    // Test case 2: demo.jpg
    std::string rasterPath2 = getDataPath("demo.jpg");
    double gt2[6] = {0};
    bool result2 = readGeoTransformReal(rasterPath2, gt2);
    double expected2_gt[6] = {0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
    bool expected2_success = true;
    bool passed2 = (result2 == expected2_success) && isEqualGT(gt2, expected2_gt, 0.01);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (!passed2) {
        std::cout << "  Actual success: " << (result2 ? "true" : "false") << std::endl;
        std::cout << "  Actual geotransform: ";
        std::cout << std::fixed << std::setprecision(1);
        std::cout << "[" << gt2[0] << ", " << gt2[1] << ", " << gt2[2] << ", " 
                  << gt2[3] << ", " << gt2[4] << ", " << gt2[5] << "]" << std::endl;
        std::cout.unsetf(std::ios_base::fixed);
    }
    if (passed2) correctCount++;
    
    // Test case 3: p2.png
    std::string rasterPath3 = getDataPath("p2.png");
    double gt3[6] = {0};
    bool result3 = readGeoTransformReal(rasterPath3, gt3);
    double expected3_gt[6] = {100.0, 2.5, 0.0, 200.0, 0.0, -2.5};
    bool expected3_success = true;
    bool passed3 = (result3 == expected3_success) && isEqualGT(gt3, expected3_gt, 0.01);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (!passed3) {
        std::cout << "  Actual success: " << (result3 ? "true" : "false") << std::endl;
        std::cout << "  Actual geotransform: ";
        std::cout << std::fixed << std::setprecision(1);
        std::cout << "[" << gt3[0] << ", " << gt3[1] << ", " << gt3[2] << ", " 
                  << gt3[3] << ", " << gt3[4] << ", " << gt3[5] << "]" << std::endl;
        std::cout.unsetf(std::ios_base::fixed);
    }
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 5.2: Completely correct!" << std::endl;
    }
    
    return 0;
}

step_number: 4
step_description_prompt: Please write a C++ function named readRasterSubset that reads a subset of a raster file based on given offsets and sizes, returning a 2D vector of doubles. Function definition: std::vector<std::vector<double>> readRasterSubset(const std::string& filepath, int xoff, int yoff, int xsize, int ysize, int band = 1); Algorithm flow: 1. Check if the file exists using the predefined fileExists function. 2. If the file does not exist, return an empty 2D vector. 3. For the simplified version, return an empty 2D vector directly (full implementation requires the GDAL library). The fileExists function used is predefined and does not need to be redefined. Note: This function involves reading raster files; the full implementation requires the GDAL library for actual raster data handling.
function_header: std::vector<std::vector<double>> readRasterSubset( const std::string& filepath, int xoff, int yoff, int xsize, int ysize, int band = 1 )
test_cases: int main() {
    // Get relative path (from problems directory to data directory)
    auto getDataPath = [](const std::string& relativePath) -> std::string {
        return "../test_data/gisdata/" + relativePath;
    };
    
    std::cout << "Subproblem 5.4: Read raster subset" << std::endl;
    std::cout << "===================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: Check if function returns empty vector (simplified version)
    std::string rasterPath1 = getDataPath("landsat/20180523.img");
    auto result1 = readRasterSubset(rasterPath1, 0, 0, 100, 100);
    std::vector<std::vector<double>> expected1;  // Expected: empty vector
    bool passed1 = (result1.size() == expected1.size() && result1 == expected1);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (passed1) correctCount++;
    
    // Test case 2: Check if function returns empty vector with different parameters
    std::string rasterPath2 = "../test_data/gisdata/landsat/20180523.img";
    auto result2 = readRasterSubset(rasterPath2, 0, 0, 50, 50);
    std::vector<std::vector<double>> expected2;  // Expected: empty vector
    bool passed2 = (result2.size() == expected2.size() && result2 == expected2);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (passed2) correctCount++;
    
    // Test case 3: Check if function returns empty vector with different offset
    auto result3 = readRasterSubset(rasterPath1, 10, 10, 200, 200);
    std::vector<std::vector<double>> expected3;  // Expected: empty vector
    bool passed3 = (result3.size() == expected3.size() && result3 == expected3);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 5.4: Completely correct!" << std::endl;
    }
    
    return 0;
}


step_number: 5
step_description_prompt: Please write a C++ function named saveRaster that implements saving a raster (simplified version: only checks if the file can be created; full implementation requires the GDAL library). The function definition is bool saveRaster(const std::string& filepath, const std::vector<std::vector<double>>& raster, const double* geotransform = nullptr, const std::string& projection = ""). The simplified version only checks if the file can be created and returns true (full implementation requires GDAL). Any called functions are already defined.
function_header: bool saveRaster( const std::string& filepath, const std::vector<std::vector<double>>& raster, const double* geotransform = nullptr, const std::string& projection = "" )
test_cases: int main() {
    std::cout << "Subproblem 5.5: Save raster" << std::endl;
    std::cout << "===========================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    std::vector<std::vector<double>> testRaster1 = {
        {1.0, 2.0, 3.0},
        {4.0, 5.0, 6.0},
        {7.0, 8.0, 9.0}
    };
    double testGT1[6] = {0, 1, 0, 0, 0, -1};
    
    // Test case 1: Check if function returns true (simplified version)
    bool result1 = saveRaster("../test_data/gisdata/test_output.tif", testRaster1, testGT1);
    bool expected1 = true;  // Expected: true
    bool passed1 = (result1 == expected1);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (passed1) correctCount++;
    
    std::vector<std::vector<double>> testRaster2 = {
        {10.0, 20.0},
        {30.0, 40.0}
    };
    double testGT2[6] = {100, 10, 0, 200, 0, -10};
    
    // Test case 2: Check if function returns true with different raster
    bool result2 = saveRaster("../test_data/gisdata/test_output2.tif", testRaster2, testGT2);
    bool expected2 = true;  // Expected: true
    bool passed2 = (result2 == expected2);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (passed2) correctCount++;
    
    std::vector<std::vector<double>> testRaster3 = {{5.0}};
    
    // Test case 3: Check if function returns true with minimal raster
    bool result3 = saveRaster("../test_data/gisdata/test_output3.tif", testRaster3, nullptr);
    bool expected3 = true;  // Expected: true
    bool passed3 = (result3 == expected3);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 5.5: Completely correct!" << std::endl;
    }
    
    return 0;
}


step_number: 6
step_description_prompt: Please write a C++ function named calculateRasterStats that takes a 2D vector of doubles (const std::vector<std::vector<double>>& raster) as input and returns a vector of doubles containing eight raster statistics: min, max, mean, median, 25th percentile (q25), 75th percentile (q75), variance, and standard deviation. The function signature must be std::vector<double> calculateRasterStats(const std::vector<std::vector<double>>& raster). First, collect all values from the input raster into a 1D vector, compute the sum, min (initialized to std::numeric_limits<double>::max()), and max (initialized to std::numeric_limits<double>::lowest()) of these values. Calculate the mean as sum divided by the total number of values. Compute variance as the average of squared differences from the mean, and standard deviation as the square root of variance. Sort the collected values using std::sort. Compute median, q25, and q75 using linear interpolation: for a percentile p, calculate index = p * (sorted values size - 1), get lower (std::floor(index)) and upper (std::ceil(index)) indices; if lower != upper, use weighted average (sorted[lower]*(1 - (index - lower)) + sorted[upper]*(index - lower)); if lower == upper, use sorted[lower]. If the input raster is empty or any row is empty, return a vector of eight 0.0 values. Return a vector containing min, max, mean, median, q25, q75, variance, stddev in this exact order. Used functions like std::sort, std::floor, std::ceil, std::sqrt, and std::numeric_limits are assumed to be already defined.
function_header: std::vector<double> calculateRasterStats(const std::vector<std::vector<double>>& raster)
test_cases: int main() {
    // Comparison function
    auto isEqual = [](double a, double b, double tolerance = 1e-6) {
        return std::abs(a - b) <= tolerance;
    };
    
    std::cout << "Subproblem 5.6: Raster statistics (Enhanced: Advanced Statistics)" << std::endl;
    std::cout << "=================================================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: Simple raster
    std::vector<std::vector<double>> raster1 = {
        {1.0, 2.0, 3.0},
        {4.0, 5.0, 6.0},
        {7.0, 8.0, 9.0}
    };
    auto stats1 = calculateRasterStats(raster1);
    bool passed1 = isEqual(stats1[0], 1.0, 0.01) &&
                   isEqual(stats1[1], 9.0, 0.01) &&
                   isEqual(stats1[2], 5.0, 0.01) &&
                   isEqual(stats1[3], 5.0, 0.01);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (passed1) correctCount++;
    
    // Test case 2: Larger raster
    std::vector<std::vector<double>> raster2(10, std::vector<double>(10, 5.0));
    auto stats2 = calculateRasterStats(raster2);
    bool passed2 = isEqual(stats2[0], 5.0, 0.01) &&
                   isEqual(stats2[1], 5.0, 0.01) &&
                   isEqual(stats2[2], 5.0, 0.01) &&
                   isEqual(stats2[6], 0.0, 0.01);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (passed2) correctCount++;
    
    // Test case 3: Raster with variance
    std::vector<std::vector<double>> raster3 = {
        {1.0, 2.0},
        {3.0, 4.0}
    };
    auto stats3 = calculateRasterStats(raster3);
    bool passed3 = isEqual(stats3[0], 1.0, 0.01) &&
                   isEqual(stats3[1], 4.0, 0.01) &&
                   isEqual(stats3[2], 2.5, 0.01) &&
                   stats3[6] > 0.0;
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 5.6: Completely correct!" << std::endl;
    }
    
    return 0;
}


General solution:

General tests: