LCOV - code coverage report
Current view: top level - autotest/cpp - test_ogr_organize_polygons.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 117 119 98.3 %
Date: 2025-07-09 17:50:03 Functions: 38 38 100.0 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Test OGRGeometryFactory::organizePolygons
       5             :  * Author:   Daniel Baston <dbaston at gmail.com>
       6             :  *
       7             :  **********************************************************************
       8             :  * Copyright (c) 2023, ISciences LLC
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_unit_test.h"
      14             : #include "cpl_string.h"
      15             : #include "ogr_geometry.h"
      16             : #include "gtest_include.h"
      17             : 
      18             : #include <vector>
      19             : 
      20             : #if defined(__clang__)
      21             : #pragma clang diagnostic push
      22             : #pragma clang diagnostic ignored "-Wweak-vtables"
      23             : #endif
      24             : 
      25             : static std::unique_ptr<OGRGeometry>
      26          33 : organizePolygons(std::vector<OGRGeometry *> &polygons,
      27             :                  const std::string &method)
      28             : {
      29          66 :     CPLStringList options;
      30          33 :     options.AddNameValue("METHOD", method.c_str());
      31             : 
      32             :     return std::unique_ptr<OGRGeometry>(OGRGeometryFactory::organizePolygons(
      33          33 :         polygons.data(), static_cast<int>(polygons.size()), nullptr,
      34          99 :         (const char **)options.List()));
      35             : }
      36             : 
      37          70 : static OGRGeometry *readWKT(const std::string &wkt)
      38             : {
      39             :     OGRGeometry *g;
      40          70 :     auto err = OGRGeometryFactory::createFromWkt(wkt.c_str(), nullptr, &g);
      41             : 
      42          70 :     if (err != OGRERR_NONE)
      43             :     {
      44           0 :         throw std::runtime_error("Failed to parse WKT");
      45             :     }
      46             : 
      47          70 :     return g;
      48             : }
      49             : 
      50             : class OrganizePolygonsTest : public testing::TestWithParam<std::string>
      51             : {
      52             : };
      53             : 
      54          88 : INSTANTIATE_TEST_SUITE_P(
      55             :     test_gdal, OrganizePolygonsTest,
      56             :     ::testing::Values("DEFAULT", "ONLY_CCW", "SKIP"),
      57             :     [](const ::testing::TestParamInfo<std::string> &param_info) -> std::string
      58             :     { return param_info.param; });
      59             : 
      60           7 : TEST_P(OrganizePolygonsTest, EmptyInputVector)
      61             : {
      62           3 :     std::vector<OGRGeometry *> polygons;
      63             : 
      64           3 :     const auto &method = GetParam();
      65           3 :     auto result = organizePolygons(polygons, method);
      66             : 
      67           3 :     ASSERT_NE(result, nullptr);
      68           3 :     ASSERT_EQ(result->getGeometryType(), wkbPolygon);
      69           3 :     ASSERT_TRUE(result->IsEmpty());
      70             : }
      71             : 
      72           7 : TEST_P(OrganizePolygonsTest, SinglePolygonInput)
      73             : {
      74           3 :     std::vector<OGRGeometry *> polygons;
      75           3 :     polygons.push_back(readWKT("POLYGON ((0 0, 1 0, 1 1, 0 0))"));
      76             : 
      77           3 :     std::unique_ptr<OGRGeometry> expected(polygons.front()->clone());
      78             : 
      79           3 :     const auto &method = GetParam();
      80           3 :     auto result = organizePolygons(polygons, method);
      81             : 
      82           3 :     ASSERT_NE(result, nullptr);
      83           3 :     ASSERT_EQ(result->getGeometryType(), wkbPolygon);
      84           3 :     ASSERT_TRUE(result->Equals(expected.get()));
      85             : }
      86             : 
      87           7 : TEST_P(OrganizePolygonsTest, SingleCurvePolygonInput)
      88             : {
      89           3 :     std::vector<OGRGeometry *> polygons;
      90           3 :     polygons.push_back(readWKT("CURVEPOLYGON ((0 0, 1 0, 1 1, 0 0))"));
      91             : 
      92           3 :     std::unique_ptr<OGRGeometry> expected(polygons.front()->clone());
      93             : 
      94           3 :     const auto &method = GetParam();
      95           3 :     auto result = organizePolygons(polygons, method);
      96             : 
      97           3 :     ASSERT_NE(result, nullptr);
      98           3 :     ASSERT_EQ(result->getGeometryType(), wkbCurvePolygon);
      99           3 :     ASSERT_TRUE(result->Equals(expected.get()));
     100             : }
     101             : 
     102           7 : TEST_P(OrganizePolygonsTest, SinglePointInput)
     103             : {
     104           3 :     std::vector<OGRGeometry *> polygons;
     105           3 :     polygons.push_back(readWKT("POINT (0 0)"));
     106             : 
     107           3 :     const auto &method = GetParam();
     108           3 :     auto result = organizePolygons(polygons, method);
     109             : 
     110           3 :     ASSERT_NE(result, nullptr);
     111           3 :     ASSERT_EQ(result->getGeometryType(), wkbPolygon);
     112           3 :     ASSERT_TRUE(result->IsEmpty());
     113             : }
     114             : 
     115           7 : TEST_P(OrganizePolygonsTest, MixedPolygonCurvePolygonInput)
     116             : {
     117           3 :     std::vector<OGRGeometry *> polygons;
     118           3 :     polygons.push_back(
     119           3 :         readWKT("POLYGON ((10 10, 20 10, 20 20, 20 10, 10 10))"));
     120           3 :     polygons.push_back(readWKT("CURVEPOLYGON ((0 0, 1 0, 1 1, 0 0))"));
     121             : 
     122           3 :     const auto &method = GetParam();
     123           3 :     auto result = organizePolygons(polygons, method);
     124             : 
     125           3 :     ASSERT_NE(result, nullptr);
     126           3 :     ASSERT_EQ(result->getGeometryType(), wkbMultiSurface);
     127             : 
     128             :     std::unique_ptr<OGRGeometry> expected(
     129             :         readWKT("MULTISURFACE ("
     130             :                 "POLYGON ((10 10, 20 10, 20 20, 20 10, 10 10)),"
     131           6 :                 "CURVEPOLYGON ((0 0, 1 0, 1 1, 0 0)))"));
     132             : 
     133           3 :     ASSERT_TRUE(result->Equals(expected.get()));
     134             : }
     135             : 
     136           7 : TEST_P(OrganizePolygonsTest, MixedPolygonPointInput)
     137             : {
     138           3 :     std::vector<OGRGeometry *> polygons;
     139           3 :     polygons.push_back(readWKT("POLYGON ((0 0, 1 0, 1 1, 0 0))"));
     140           3 :     polygons.push_back(readWKT("POINT (2 2)"));
     141             : 
     142           3 :     std::unique_ptr<OGRGeometry> expected(polygons[0]->clone());
     143             : 
     144           3 :     const auto &method = GetParam();
     145           3 :     auto result = organizePolygons(polygons, method);
     146             : 
     147           3 :     ASSERT_NE(result, nullptr);
     148           3 :     ASSERT_TRUE(result->Equals(expected.get()));
     149             : }
     150             : 
     151           7 : TEST_P(OrganizePolygonsTest, CWPolygonCCWHole)
     152             : {
     153           3 :     std::vector<OGRGeometry *> polygons;
     154           3 :     polygons.push_back(readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))"));
     155           3 :     polygons.push_back(readWKT("POLYGON ((1 1, 2 1, 2 2, 1 2, 1 1))"));
     156             : 
     157           3 :     const auto &method = GetParam();
     158           3 :     auto result = organizePolygons(polygons, method);
     159             : 
     160           3 :     ASSERT_NE(result, nullptr);
     161             : 
     162           0 :     std::unique_ptr<OGRGeometry> expected;
     163           3 :     if (method == "SKIP")
     164             :     {
     165           1 :         expected.reset(readWKT("MULTIPOLYGON (((0 0, 0 10, 10 10, 10 0, 0 0)), "
     166             :                                "((1 1, 2 1, 2 2, 1 2, 1 1)))"));
     167             :     }
     168             :     else
     169             :     {
     170           2 :         expected.reset(readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, "
     171             :                                "2 1, 2 2, 1 2, 1 1))"));
     172             :     }
     173             : 
     174           3 :     ASSERT_TRUE(result->Equals(expected.get()));
     175             : }
     176             : 
     177           7 : TEST_P(OrganizePolygonsTest, CWPolygonCCWLakeCWIslandInLake)
     178             : {
     179           3 :     std::vector<OGRGeometry *> polygons;
     180           3 :     polygons.push_back(
     181           3 :         readWKT("POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0))"));  // CW
     182           3 :     polygons.push_back(
     183           3 :         readWKT("POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))"));  // CCW
     184           3 :     polygons.push_back(
     185           3 :         readWKT("POLYGON ((15 15, 15 16, 16 16, 16 15, 15 15))"));  // CW
     186             : 
     187           3 :     const auto &method = GetParam();
     188           3 :     auto result = organizePolygons(polygons, method);
     189             : 
     190           3 :     ASSERT_NE(result, nullptr);
     191             : 
     192           3 :     if (method != "SKIP")
     193             :     {
     194             :         std::unique_ptr<OGRGeometry> expected(
     195             :             readWKT("MULTIPOLYGON ("
     196             :                     "((0 0, 0 100, 100 100, 100 0, 0 0), (10 10, 20 10, 20 20, "
     197             :                     "10 20, 10 10)),"
     198           4 :                     "((15 15, 15 16, 16 16, 16 15, 15 15)))"));
     199           2 :         ASSERT_TRUE(result->Equals(expected.get()));
     200             :     }
     201             : }
     202             : 
     203           7 : TEST_P(OrganizePolygonsTest, AdjacentCCWPolygons)
     204             : {
     205           3 :     std::vector<OGRGeometry *> polygons;
     206           3 :     polygons.push_back(readWKT("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"));  // CCW
     207           3 :     polygons.push_back(readWKT("POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))"));  // CCW
     208             : 
     209           3 :     const auto &method = GetParam();
     210           3 :     auto result = organizePolygons(polygons, method);
     211             : 
     212           3 :     ASSERT_NE(result, nullptr);
     213             : 
     214             :     std::unique_ptr<OGRGeometry> expected(
     215             :         readWKT("MULTIPOLYGON("
     216             :                 "((0 0, 1 0, 1 1, 0 1, 0 0)), "
     217           6 :                 "((1 0, 2 0, 2 1, 1 1, 1 0)))"));
     218           3 :     ASSERT_TRUE(result->Equals(expected.get()));
     219             : }
     220             : 
     221           7 : TEST_P(OrganizePolygonsTest, HoleAlongEdge)
     222             : {
     223           3 :     std::vector<OGRGeometry *> polygons;
     224           3 :     polygons.push_back(
     225           3 :         readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))"));             // CW
     226           3 :     polygons.push_back(readWKT("POLYGON ((0 2, 1 2, 1 3, 0 3, 0 2))"));  // CCW
     227             : 
     228           3 :     const auto &method = GetParam();
     229           3 :     auto result = organizePolygons(polygons, method);
     230             : 
     231           3 :     ASSERT_NE(result, nullptr);
     232             : 
     233           3 :     if (method != "SKIP")
     234             :     {
     235             :         std::unique_ptr<OGRGeometry> expected(
     236             :             readWKT("POLYGON("
     237             :                     "(0 0, 0 10, 10 10, 10 0, 0 0), "
     238           4 :                     "(0 2, 1 2, 1 3, 0 3, 0 2))"));
     239           2 :         ASSERT_TRUE(result->Equals(expected.get()));
     240             :     }
     241             : }
     242             : 
     243           7 : TEST_P(OrganizePolygonsTest, CrossingCCWPolygons)
     244             : {
     245           3 :     std::vector<OGRGeometry *> polygons;
     246           3 :     polygons.push_back(readWKT("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"));
     247           3 :     polygons.push_back(readWKT("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))"));
     248             : 
     249           3 :     const auto &method = GetParam();
     250           3 :     auto result = organizePolygons(polygons, method);
     251             : 
     252           3 :     ASSERT_NE(result, nullptr);
     253             : 
     254             :     std::unique_ptr<OGRGeometry> expected(
     255             :         readWKT("MULTIPOLYGON("
     256             :                 "((0 0, 10 0, 10 10, 0 10, 0 0)), "
     257           6 :                 "((5 5, 15 5, 15 15, 5 15, 5 5)))"));
     258           3 :     ASSERT_TRUE(result->Equals(expected.get()));
     259             : }
     260             : 
     261             : #if defined(__clang__)
     262             : #pragma clang diagnostic pop
     263             : #endif

Generated by: LCOV version 1.14