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