Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_clip_common.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Common code for gdalalg_raster_clip and gdalalg_vector_clip
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_clip_common.h"
14
15
#include "ogrsf_frmts.h"
16
17
//! @cond Doxygen_Suppress
18
19
#ifndef _
20
#define _(x) (x)
21
#endif
22
23
/************************************************************************/
24
/*                          ~GDALClipCommon()                           */
25
/************************************************************************/
26
27
0
GDALClipCommon::~GDALClipCommon() = default;
28
29
/************************************************************************/
30
/*                            LoadGeometry()                            */
31
/************************************************************************/
32
33
std::pair<std::unique_ptr<OGRGeometry>, std::string>
34
GDALClipCommon::LoadGeometry()
35
0
{
36
0
    auto poDS = m_likeDataset.GetDatasetRef();
37
0
    OGRLayer *poLyr = nullptr;
38
0
    if (!m_likeSQL.empty())
39
0
        poLyr = poDS->ExecuteSQL(m_likeSQL.c_str(), nullptr, nullptr);
40
0
    else if (!m_likeLayer.empty())
41
0
        poLyr = poDS->GetLayerByName(m_likeLayer.c_str());
42
0
    else
43
0
        poLyr = poDS->GetLayer(0);
44
45
0
    if (poLyr == nullptr)
46
0
    {
47
0
        return {nullptr,
48
0
                "Failed to identify source layer from clipping dataset."};
49
0
    }
50
51
0
    if (!m_likeWhere.empty())
52
0
        poLyr->SetAttributeFilter(m_likeWhere.c_str());
53
54
0
    OGRGeometryCollection oGC;
55
0
    oGC.assignSpatialReference(poLyr->GetSpatialRef());
56
57
0
    for (auto &poFeat : poLyr)
58
0
    {
59
0
        auto poSrcGeom = std::unique_ptr<OGRGeometry>(poFeat->StealGeometry());
60
0
        if (poSrcGeom)
61
0
        {
62
            // Only take into account areal geometries.
63
0
            if (poSrcGeom->getDimension() == 2)
64
0
            {
65
0
                if (!poSrcGeom->IsValid())
66
0
                {
67
0
                    return {
68
0
                        nullptr,
69
0
                        CPLSPrintf("Geometry of feature " CPL_FRMT_GIB " of %s "
70
0
                                   "is invalid. You may be able to correct it "
71
0
                                   "with 'gdal vector geom make-valid'.",
72
0
                                   poFeat->GetFID(), poDS->GetDescription())};
73
0
                }
74
0
                else
75
0
                {
76
0
                    oGC.addGeometry(std::move(poSrcGeom));
77
0
                }
78
0
            }
79
0
            else
80
0
            {
81
0
                CPLErrorOnce(CE_Warning, CPLE_AppDefined,
82
0
                             "Non-polygonal geometry encountered in clipping "
83
0
                             "dataset will be ignored.");
84
0
            }
85
0
        }
86
0
    }
87
88
0
    if (!m_likeSQL.empty())
89
0
        poDS->ReleaseResultSet(poLyr);
90
91
0
    if (oGC.IsEmpty())
92
0
    {
93
0
        return {nullptr, "No clipping geometry found"};
94
0
    }
95
96
0
    return {std::unique_ptr<OGRGeometry>(oGC.UnaryUnion()), std::string()};
97
0
}
98
99
/************************************************************************/
100
/*                          GetClipGeometry()                           */
101
/************************************************************************/
102
103
std::pair<std::unique_ptr<OGRGeometry>, std::string>
104
GDALClipCommon::GetClipGeometry()
105
0
{
106
107
0
    std::unique_ptr<OGRGeometry> poClipGeom;
108
109
0
    if (!m_bbox.empty())
110
0
    {
111
0
        poClipGeom = std::make_unique<OGRPolygon>(m_bbox[0], m_bbox[1],
112
0
                                                  m_bbox[2], m_bbox[3]);
113
114
0
        if (!m_bboxCrs.empty())
115
0
        {
116
0
            auto poSRS = new OGRSpatialReference();
117
0
            poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
118
0
            CPL_IGNORE_RET_VAL(poSRS->SetFromUserInput(m_bboxCrs.c_str()));
119
0
            poClipGeom->assignSpatialReference(poSRS);
120
0
            poSRS->Release();
121
0
        }
122
0
    }
123
0
    else if (!m_geometry.empty())
124
0
    {
125
0
        {
126
0
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
127
0
            auto [poGeom, eErr] =
128
0
                OGRGeometryFactory::createFromWkt(m_geometry.c_str());
129
0
            if (eErr == OGRERR_NONE)
130
0
            {
131
0
                poClipGeom = std::move(poGeom);
132
0
            }
133
0
            else
134
0
            {
135
0
                poClipGeom.reset(
136
0
                    OGRGeometryFactory::createFromGeoJson(m_geometry.c_str()));
137
0
                if (poClipGeom && poClipGeom->getSpatialReference() == nullptr)
138
0
                {
139
0
                    auto poSRS = new OGRSpatialReference();
140
0
                    poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
141
0
                    CPL_IGNORE_RET_VAL(poSRS->SetFromUserInput("WGS84"));
142
0
                    poClipGeom->assignSpatialReference(poSRS);
143
0
                    poSRS->Release();
144
0
                }
145
0
            }
146
0
        }
147
0
        if (!poClipGeom)
148
0
        {
149
0
            return {
150
0
                nullptr,
151
0
                "Clipping geometry is neither a valid WKT or GeoJSON geometry"};
152
0
        }
153
154
0
        if (!m_geometryCrs.empty())
155
0
        {
156
0
            auto poSRS = new OGRSpatialReference();
157
0
            poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
158
            // Validity of CRS already checked by GDALAlgorithm
159
0
            CPL_IGNORE_RET_VAL(poSRS->SetFromUserInput(m_geometryCrs.c_str()));
160
0
            poClipGeom->assignSpatialReference(poSRS);
161
0
            poSRS->Release();
162
0
        }
163
0
    }
164
0
    else if (auto poLikeDS = m_likeDataset.GetDatasetRef())
165
0
    {
166
0
        if (poLikeDS->GetLayerCount() > 1 && m_likeLayer.empty() &&
167
0
            m_likeSQL.empty())
168
0
        {
169
0
            return {
170
0
                nullptr,
171
0
                "Only single layer dataset can be specified with --like when "
172
0
                "neither --like-layer or --like-sql have been specified"};
173
0
        }
174
0
        else if (poLikeDS->GetLayerCount() > 0)
175
0
        {
176
0
            std::string errMsg;
177
0
            std::tie(poClipGeom, errMsg) = LoadGeometry();
178
0
            if (!poClipGeom)
179
0
                return {nullptr, errMsg};
180
0
        }
181
0
        else if (poLikeDS->GetRasterCount() > 0)
182
0
        {
183
0
            GDALGeoTransform gt;
184
0
            if (poLikeDS->GetGeoTransform(gt) != CE_None)
185
0
            {
186
0
                return {
187
0
                    nullptr,
188
0
                    CPLSPrintf(
189
0
                        "Dataset '%s' has no geotransform matrix. Its bounds "
190
0
                        "cannot be established.",
191
0
                        poLikeDS->GetDescription())};
192
0
            }
193
0
            auto poLikeSRS = poLikeDS->GetSpatialRef();
194
0
            const double dfTLX = gt.xorig;
195
0
            const double dfTLY = gt.yorig;
196
197
0
            double dfTRX = 0;
198
0
            double dfTRY = 0;
199
0
            gt.Apply(poLikeDS->GetRasterXSize(), 0, &dfTRX, &dfTRY);
200
201
0
            double dfBLX = 0;
202
0
            double dfBLY = 0;
203
0
            gt.Apply(0, poLikeDS->GetRasterYSize(), &dfBLX, &dfBLY);
204
205
0
            double dfBRX = 0;
206
0
            double dfBRY = 0;
207
0
            gt.Apply(poLikeDS->GetRasterXSize(), poLikeDS->GetRasterYSize(),
208
0
                     &dfBRX, &dfBRY);
209
210
0
            auto poPoly = std::make_unique<OGRPolygon>();
211
0
            auto poLR = std::make_unique<OGRLinearRing>();
212
0
            poLR->addPoint(dfTLX, dfTLY);
213
0
            poLR->addPoint(dfTRX, dfTRY);
214
0
            poLR->addPoint(dfBRX, dfBRY);
215
0
            poLR->addPoint(dfBLX, dfBLY);
216
0
            poLR->addPoint(dfTLX, dfTLY);
217
0
            poPoly->addRingDirectly(poLR.release());
218
0
            poPoly->assignSpatialReference(poLikeSRS);
219
0
            poClipGeom = std::move(poPoly);
220
0
        }
221
0
        else
222
0
        {
223
0
            return {nullptr, "Cannot get extent from clip dataset"};
224
0
        }
225
0
    }
226
0
    else
227
0
    {
228
0
        return {nullptr, "--bbox, --geometry or --like must be specified"};
229
0
    }
230
231
0
    return {std::move(poClipGeom), std::string()};
232
0
}
233
234
//! @endcond