Problem: C++ Shapefile Operations and Spatial Queries

The problem aims to implement core GIS functionality in C++ for Shapefile processing—including reading metadata, point coordinates, attributes, and creating point Shapefiles—and spatial queries like point-in-polygon (Winding Number) and line-polygon intersection, enabling basic GIS data handling and analysis.


Input/Output: The problem takes file paths (const std::string&) for Shapefile reading, attribute field names (const std::string&), output paths (const std::string&) for Shapefile creation, point coordinates (std::vector<std::pair<double, double>>), attribute data (std::map<std::string, std::vector<std::string>>), polygon vertices (std::vector<std::pair<double, double>>), and line segments (std::vector<std::pair<double, double>>). It processes inputs through case-insensitive Shapefile existence checks, directory path extraction for output, and spatial algorithms (Winding Number for point-in-polygon, line segment intersection for line-polygon queries). The problem outputs simplified Shapefile metadata (std::map<std::string, std::string>), point coordinate vectors, attribute value vectors (std::vector<std::string>), a boolean for Shapefile creation status, and booleans for spatial query results (point-in-polygon, line-polygon intersection). Test cases validate outputs against expected results like file existence and spatial query correctness.

Required dependencies:
#ifndef _USE_MATH_DEFINES
#define _USE_MATH_DEFINES
#endif
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <cctype>
#include <cmath>
#include <io.h>
#include <utility>
using namespace std;

Sub-steps:
step_number: 1
step_description_prompt: Please write a C++ function named readShapefileMetadata that checks the existence of a Shapefile and returns its simplified metadata schema (a std::map<std::string, std::string>). The function signature is std::map<std::string, std::string> readShapefileMetadata(const std::string& filepath). 

Algorithm steps: 
1. Define a lambda fileExists using _access to check if a file exists. 
2. Convert the input filepath to lowercase: if it does not contain ".shp", append ".shp" to form shpPath. 
3. Check if shpPath exists. If not, convert the original filepath to uppercase: if it does not contain ".SHP", append ".SHP". Check if this uppercase path exists. 
4. If the file exists (either shpPath or uppercase path), set metadata.featureCount to -1 (indicates file exists but metadata not fully read). 
5. Return metadata.schema (a std::map<std::string, std::string>). 

Note: The function uses _access to check file existence. Handle both .shp and .SHP extensions by modifying the filepath case and appending the extension if missing. Dependent functions (like _access) are pre-defined.
function_header: std::map<std::string, std::string> readShapefileMetadata(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 4.1: Read Shapefile metadata" << std::endl;
    std::cout << "========================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: Check if function returns empty map (simplified version)
    std::string path1 = getDataPath("pm10_pts/pm10_pts.shp");
    auto result1 = readShapefileMetadata(path1);
    std::map<std::string, std::string> expected1;  // Expected: empty map
    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 map for capitals
    std::string path2 = getDataPath("capitals/CAPITALS.SHP");
    auto result2 = readShapefileMetadata(path2);
    std::map<std::string, std::string> expected2;  // Expected: empty map
    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 map for states
    std::string path3 = getDataPath("states/STATES.SHP");
    auto result3 = readShapefileMetadata(path3);
    std::map<std::string, std::string> expected3;  // Expected: empty map
    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 4.1: Completely correct!" << std::endl;
    }
    
    return 0;
}

step_number: 2
step_description_prompt: Please write a C++ function named readPointCoordinates that takes a const std::string& filepath as input and returns a std::vector<std::pair<double, double>> for reading 2D point feature coordinates from a shapefile. The function definition is std::vector<std::pair<double, double>> readPointCoordinates(const std::string& filepath).  

Algorithm steps: 1. Define an internal struct Point2D with double x/y, default (x=0.0, y=0.0) and parameterized constructors. 2. Define a lambda fileExists (uses _access to check file existence). 3. Initialize a std::vector<std::pair<double, double>> points. 4. Process filepath: create shpPath, convert filepath to lowercase—if no ".shp", append ".shp" to shpPath. 5. Check shpPath existence; if not, convert filepath to uppercase—if no ".SHP", append ".SHP" and check. If neither exists, return empty points. 6. Return empty points (full version needs GDAL/OGR).  

Note: The function handles shapefile reading with case-insensitive ".shp" checks. The _access and std::transform are pre-defined.
function_header: std::vector<std::pair<double, double>> readPointCoordinates(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 4.2: Read point feature coordinates" << std::endl;
    std::cout << "===============================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: Check if function returns empty vector (simplified version)
    std::string path1 = getDataPath("pm10_pts/pm10_pts.shp");
    auto result1 = readPointCoordinates(path1);
    std::vector<std::pair<double, 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 for capitals
    std::string path2 = getDataPath("capitals/CAPITALS.SHP");
    auto result2 = readPointCoordinates(path2);
    std::vector<std::pair<double, 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 for Uscities_WGS
    std::string path3 = getDataPath("Uscities_WGS/Uscities_WGS.shp");
    auto result3 = readPointCoordinates(path3);
    std::vector<std::pair<double, 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 4.2: Completely correct!" << std::endl;
    }
    
    return 0;
}

step_number: 3
step_description_prompt: Please write a C++ function named readAttributeField that reads attribute data of a specified field from a shapefile (simplified version). The function takes two std::string parameters: filepath (the path to the shapefile) and fieldName (the name of the attribute field to read), and returns a std::vector<std::string> containing the field values.
Function definition: std::vector<std::string> readAttributeField(const std::string& filepath, const std::string& fieldName);
Algorithm steps: 1. Define a lambda function fileExists to check file existence using _access. 2. Initialize an empty std::vector<std::string> values. 3. Process filepath: if it doesn't end with .shp (case-insensitive), append .shp to get shpPath. 4. Check if shpPath exists; if not, create upperPath by converting filepath to uppercase, append .SHP if needed, and check existence. 5. If no valid file exists, return empty values. 6. (Simplified) Return empty values regardless of file existence.
Note: The function involves shapefile existence checks. Assume _access, necessary headers (<vector>, <string>, <algorithm>, <cctype>), and related functions are already available. This is a simplified version that does not actually read attribute data.
function_header: std::vector<std::string> readAttributeField(const std::string& filepath, const std::string& fieldName)
test_cases: int main() {
    // Comparison function for pairs
    auto isEqualPair = [](const std::pair<double, double>& a, const std::pair<double, double>& b, double tolerance = 1e-6) {
        return std::abs(a.first - b.first) <= tolerance && std::abs(a.second - b.second) <= tolerance;
    };
    
    // Comparison function for vector of pairs
    auto isEqualVec = [&isEqualPair](const std::vector<std::pair<double, double>>& a, const std::vector<std::pair<double, double>>& b, double tolerance = 1e-6) {
        if (a.size() != b.size()) return false;
        for (size_t i = 0; i < a.size(); ++i) {
            if (!isEqualPair(a[i], b[i], tolerance)) 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 point coordinates from CSV file
    auto readPointCoordinatesReal = [](const std::string& csvPath) -> std::vector<std::pair<double, double>> {
        std::vector<std::pair<double, double>> points;
        std::ifstream file(csvPath);
        if (!file.is_open()) {
            return points;
        }
        std::string line;
        bool firstLine = true;
        while (std::getline(file, line)) {
            if (firstLine) {
                firstLine = false;
                continue; // Skip header line
            }
            if (line.empty()) continue;
            std::istringstream iss(line);
            std::string xStr, yStr;
            if (std::getline(iss, xStr, ',') && std::getline(iss, yStr, ',')) {
                try {
                    double x = std::stod(xStr);
                    double y = std::stod(yStr);
                    points.push_back({x, y});
                } catch (...) {
                    // Skip invalid lines
                }
            }
        }
        file.close();
        return points;
    };
    
    std::cout << "Subproblem 4.2: Read point feature coordinates" << std::endl;
    std::cout << "===============================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: pm10_pts
    std::string path1 = getDataPath("pm10_pts_coordinates.csv");
    auto result1 = readPointCoordinatesReal(path1);
    std::vector<std::pair<double, double>> expected1 = {
        {1426980.6689179072, 3451415.2929588202},
        {1424200.6317453892, 3452856.7419655342},
        {1433833.9725806965, 3459364.7923419289},
        {1423469.6794857476, 3459561.4694037940},
        {1433202.1356232269, 3463506.2068246640}
    };
    bool passed1 = (result1.size() >= 5) && isEqualVec(
        std::vector<std::pair<double, double>>(result1.begin(), result1.begin() + 5),
        expected1, 0.01);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (!passed1) {
        std::cout << "  Actual result size: " << result1.size() << std::endl;
        if (result1.size() > 0) {
            std::cout << "  Actual result (first " << std::min(result1.size(), size_t(5)) << " points): ";
            std::cout << std::fixed << std::setprecision(10);
            for (size_t i = 0; i < std::min(result1.size(), size_t(5)); ++i) {
                std::cout << "(" << result1[i].first << ", " << result1[i].second << ")";
                if (i < std::min(result1.size(), size_t(5)) - 1) std::cout << ", ";
            }
            std::cout << std::endl;
            std::cout.unsetf(std::ios_base::fixed);
        }
    }
    if (passed1) correctCount++;
    
    // Test case 2: capitals
    std::string path2 = getDataPath("capitals_coordinates.csv");
    auto result2 = readPointCoordinatesReal(path2);
    std::vector<std::pair<double, double>> expected2 = {
        {-134.1334075928, 58.3885917664},
        {-122.8937988281, 47.0419197083},
        {-100.7673034668, 46.8054695129},
        {-112.0204010010, 46.5965194702},
        {-93.1036834717, 44.9477386475}
    };
    bool passed2 = (result2.size() >= 5) && isEqualVec(
        std::vector<std::pair<double, double>>(result2.begin(), result2.begin() + 5),
        expected2, 0.01);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (!passed2) {
        std::cout << "  Actual result size: " << result2.size() << std::endl;
        if (result2.size() > 0) {
            std::cout << "  Actual result (first " << std::min(result2.size(), size_t(5)) << " points): ";
            std::cout << std::fixed << std::setprecision(10);
            for (size_t i = 0; i < std::min(result2.size(), size_t(5)); ++i) {
                std::cout << "(" << result2[i].first << ", " << result2[i].second << ")";
                if (i < std::min(result2.size(), size_t(5)) - 1) std::cout << ", ";
            }
            std::cout << std::endl;
            std::cout.unsetf(std::ios_base::fixed);
        }
    }
    if (passed2) correctCount++;
    
    // Test case 3: Uscities_WGS
    std::string path3 = getDataPath("uscities_wgs_coordinates.csv");
    auto result3 = readPointCoordinatesReal(path3);
    std::vector<std::pair<double, double>> expected3 = {
        {-122.4681987127, 48.7438855091},
        {-109.6798679467, 48.5438252847},
        {-122.6307005487, 48.4922214881},
        {-122.3157828428, 48.4215629532},
        {-122.6289422452, 48.3079034221}
    };
    bool passed3 = (result3.size() >= 5) && isEqualVec(
        std::vector<std::pair<double, double>>(result3.begin(), result3.begin() + 5),
        expected3, 0.01);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (!passed3) {
        std::cout << "  Actual result size: " << result3.size() << std::endl;
        if (result3.size() > 0) {
            std::cout << "  Actual result (first " << std::min(result3.size(), size_t(5)) << " points): ";
            std::cout << std::fixed << std::setprecision(10);
            for (size_t i = 0; i < std::min(result3.size(), size_t(5)); ++i) {
                std::cout << "(" << result3[i].first << ", " << result3[i].second << ")";
                if (i < std::min(result3.size(), size_t(5)) - 1) std::cout << ", ";
            }
            std::cout << std::endl;
            std::cout.unsetf(std::ios_base::fixed);
        }
    }
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 4.2: Completely correct!" << std::endl;
    }
    
    return 0;
}

step_number: 4
step_description_prompt: Please use C++ code to generate a function named createPointShapefile that implements the functionality of creating a point feature Shapefile (simplified version that only checks the output path).
Function definition: bool createPointShapefile(const std::string& outputPath, const std::vector<std::pair<double, double>>& points, const std::map<std::string, std::vector<std::string>>& attributes);
Algorithm flow: Extract the directory path from outputPath by taking the substring before the last '/' or '\'; set dirPath to "." if it is empty (used to check if the output directory is writable); return true (this is a simplified version, the full implementation requires the GDAL/OGR library).
Any other functions called in the code are already defined and do not need to be redefined.
function_header: bool createPointShapefile(const std::string& outputPath, const std::vector<std::pair<double, double>>& points, const std::map<std::string, std::vector<std::string>>& attributes)
test_cases: int main() {
    std::cout << "Subproblem 4.3: Read attribute data" << std::endl;
    std::cout << "====================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Comparison function for vectors
    auto isEqualVec = [](const std::vector<std::string>& a, const std::vector<std::string>& 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 attribute field from CSV file
    auto readAttributeFieldReal = [](const std::string& csvPath, const std::string& fieldName) -> std::vector<std::string> {
        std::vector<std::string> values;
        std::ifstream file(csvPath);
        if (!file.is_open()) {
            return values;
        }
        std::string line;
        bool firstLine = true;
        while (std::getline(file, line)) {
            if (firstLine) {
                firstLine = false;
                continue; // Skip header line
            }
            if (!line.empty()) {
                values.push_back(line);
            }
        }
        file.close();
        return values;
    };
    
    // Test case 1: pm10_pts/PM10
    std::string path1 = getDataPath("pm10_pts_PM10.csv");
    auto result1 = readAttributeFieldReal(path1, "PM10");
    std::vector<std::string> expected1 = {"73", "76", "81", "78", "86"};
    bool passed1 = (result1.size() >= 5) && isEqualVec(
        std::vector<std::string>(result1.begin(), result1.begin() + 5),
        expected1);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (!passed1) {
        std::cout << "  Actual result size: " << result1.size() << std::endl;
        if (result1.size() > 0) {
            std::cout << "  Actual result (first " << std::min(result1.size(), size_t(5)) << " values): ";
            for (size_t i = 0; i < std::min(result1.size(), size_t(5)); ++i) {
                std::cout << "\"" << result1[i] << "\"";
                if (i < std::min(result1.size(), size_t(5)) - 1) std::cout << ", ";
            }
            std::cout << std::endl;
        }
    }
    if (passed1) correctCount++;
    
    // Test case 2: capitals/CITY_NAME
    std::string path2 = getDataPath("capitals_CITY_NAME.csv");
    auto result2 = readAttributeFieldReal(path2, "CITY_NAME");
    std::vector<std::string> expected2 = {"Juneau", "Olympia", "Bismarck", "Helena", "St. Paul"};
    bool passed2 = (result2.size() >= 5) && isEqualVec(
        std::vector<std::string>(result2.begin(), result2.begin() + 5),
        expected2);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (!passed2) {
        std::cout << "  Actual result size: " << result2.size() << std::endl;
        if (result2.size() > 0) {
            std::cout << "  Actual result (first " << std::min(result2.size(), size_t(5)) << " values): ";
            for (size_t i = 0; i < std::min(result2.size(), size_t(5)); ++i) {
                std::cout << "\"" << result2[i] << "\"";
                if (i < std::min(result2.size(), size_t(5)) - 1) std::cout << ", ";
            }
            std::cout << std::endl;
        }
    }
    if (passed2) correctCount++;
    
    // Test case 3: states/STATE_NAME
    std::string path3 = getDataPath("states_STATE_NAME.csv");
    auto result3 = readAttributeFieldReal(path3, "STATE_NAME");
    std::vector<std::string> expected3 = {"Washington", "Montana", "Maine", "North Dakota", "South Dakota"};
    bool passed3 = (result3.size() >= 5) && isEqualVec(
        std::vector<std::string>(result3.begin(), result3.begin() + 5),
        expected3);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (!passed3) {
        std::cout << "  Actual result size: " << result3.size() << std::endl;
        if (result3.size() > 0) {
            std::cout << "  Actual result (first " << std::min(result3.size(), size_t(5)) << " values): ";
            for (size_t i = 0; i < std::min(result3.size(), size_t(5)); ++i) {
                std::cout << "\"" << result3[i] << "\"";
                if (i < std::min(result3.size(), size_t(5)) - 1) std::cout << ", ";
            }
            std::cout << std::endl;
        }
    }
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 4.3: Completely correct!" << std::endl;
    }
    
    return 0;
}

step_number: 5
step_description_prompt: Please write a C++ function named pointInPolygon that implements the Winding Number algorithm to determine if a point is inside a polygon (including on the boundary). The function is defined as: bool pointInPolygon(const std::pair<double, double>& point, const std::vector<std::pair<double, double>>& polygon). 

Algorithm steps: 1. If the polygon has fewer than 3 vertices, return false. 2. For each edge (from polygon[i] to polygon[j], where j = (i+1) % polygon.size()): a. Check if the point is exactly on polygon[i] or polygon[j] using a tolerance of 1e-10 for floating-point comparisons; if yes, return true. b. Calculate the angle difference between the vectors (polygon[i] - point) and (polygon[j] - point) using atan2, normalize the difference to the range [-π, π] by adding/subtracting 2π as needed, and accumulate this difference to a total angle. 3. After processing all edges, return whether the absolute value of the total angle is greater than M_PI. Use M_PI for the mathematical constant π.</think>To write a C++ function named pointInPolygon that implements the Winding Number algorithm to check if a point is inside a polygon (including boundary). The function definition is: bool pointInPolygon(const std::pair<double, double>& point, const std::vector<std::pair<double, double>>& polygon).

Algorithm flow: 1. Return false if the polygon has fewer than 3 vertices. 2. For each edge (polygon[i] to polygon[j], j=(i+1)%polygon.size()): a. Check if the point is exactly on polygon[i] or polygon[j] (using 1e-10 tolerance for floating-point comparison); if yes, return true. b. Compute the angle difference between vectors from the point to polygon[i] and polygon[j] using atan2, normalize the difference to [-π, π], and accumulate to totalAngle. 3. Return whether abs(totalAngle) > M_PI. Use M_PI for π.
function_header: bool pointInPolygon( const std::pair<double, double>& point, const std::vector<std::pair<double, double>>& polygon )
test_cases: int main() {
    std::cout << "Subproblem 4.5: Point in polygon (Winding Number)" << std::endl;
    std::cout << "===================================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: Point inside square
    std::pair<double, double> point1(0.0, 0.0);
    std::vector<std::pair<double, double>> poly1 = {{-1, -1}, {1, -1}, {1, 1}, {-1, 1}, {-1, -1}};
    bool result1 = pointInPolygon(point1, poly1);
    bool passed1 = (result1 == true);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (passed1) correctCount++;
    
    // Test case 2: Point outside square
    std::pair<double, double> point2(2.0, 2.0);
    bool result2 = pointInPolygon(point2, poly1);
    bool passed2 = (result2 == false);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (passed2) correctCount++;
    
    // Test case 3: Point on edge (Winding Number handles this)
    std::pair<double, double> point3(0.5, 0.5);
    std::vector<std::pair<double, double>> poly3 = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
    bool result3 = pointInPolygon(point3, poly3);
    bool passed3 = (result3 == true);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 4.5: Completely correct!" << std::endl;
    }
    
    return 0;
}

step_number: 6
step_description_prompt: Please write a C++ function named lineIntersectsPolygon that checks if a line (vector of std::pair<double, double>, at least 2 points) intersects a polygon (vector of std::pair<double, double>, at least 3 points), returning a bool.

Function signature: bool lineIntersectsPolygon(const std::vector<std::pair<double, double>>& line, const std::vector<std::pair<double, double>>& polygon).

Algorithm:
1. Return false if line has <2 points or polygon has <3 points.
2. Define nested lambdas inside the function:
   a. pointInPolygon (point, polygon): Uses the winding number method (sum of angles around the point from polygon edges; returns true if absolute total angle > M_PI). Treats points on polygon vertices/edges as inside (compares with epsilon 1e-10). Uses a nested angleDifference lambda to compute the normalized angle between two vectors from the point.
   b. lineSegmentIntersection (p1, p2, p3, p4, intersection): Calculates intersection of two line segments via parametric equations. Returns true if parameters ua and ub are in [0, 1] (inclusive). Uses epsilon 1e-10 to check for parallel lines (denominator near zero) and floating-point comparisons.
3. For each segment in the line (from line[i] to line[i+1]):
   a. If either endpoint is inside the polygon (checked via pointInPolygon), return true.
   b. For each edge of the polygon (from polygon[j] to polygon[(j+1)%polygon.size()]):
      i. If lineSegmentIntersection finds an intersection between the line segment and polygon edge, return true.
4. Return false if no intersections are found.

Dependencies: Include <vector>, <utility>, <cmath> (define _USE_MATH_DEFINES before including <cmath> to get M_PI). Use epsilon 1e-10 for floating-point comparisons. Helper lambdas (pointInPolygon, lineSegmentIntersection, angleDifference) are defined inside lineIntersectsPolygon.
function_header: bool lineIntersectsPolygon( const std::vector<std::pair<double, double>>& line, const std::vector<std::pair<double, double>>& polygon )
test_cases: int main() {
    std::cout << "Subproblem 4.6: Line intersects polygon (precise intersection)" << std::endl;
    std::cout << "===============================================================" << std::endl << std::endl;
    
    int correctCount = 0;
    
    // Test case 1: Line intersects polygon
    std::vector<std::pair<double, double>> poly1 = {{-1, -1}, {1, -1}, {1, 1}, {-1, 1}, {-1, -1}};
    std::vector<std::pair<double, double>> line1 = {{0, 0}, {2, 2}};
    bool result1 = lineIntersectsPolygon(line1, poly1);
    bool passed1 = (result1 == true);
    std::cout << "Test case 1: " << (passed1 ? "correct" : "wrong") << std::endl;
    if (passed1) correctCount++;
    
    // Test case 2: Line outside polygon
    std::vector<std::pair<double, double>> line2 = {{2, 2}, {3, 3}};
    bool result2 = lineIntersectsPolygon(line2, poly1);
    bool passed2 = (result2 == false);
    std::cout << "Test case 2: " << (passed2 ? "correct" : "wrong") << std::endl;
    if (passed2) correctCount++;
    
    // Test case 3: Line partially inside
    std::vector<std::pair<double, double>> poly3 = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
    std::vector<std::pair<double, double>> line3 = {{0.5, 0.5}, {1.5, 1.5}};
    bool result3 = lineIntersectsPolygon(line3, poly3);
    bool passed3 = (result3 == true);
    std::cout << "Test case 3: " << (passed3 ? "correct" : "wrong") << std::endl;
    if (passed3) correctCount++;
    
    if (correctCount == 3) {
        std::cout << "Subproblem 4.6: Completely correct!" << std::endl;
    }
    
    return 0;
}


General solution:

General tests: