Skip to content

Commit 6346210

Browse files
committed
gdal vector clip: allow to not specify any of bbox, geometry or like to adjust the extent to the one of actual features
Fixes #13519
1 parent c959ec2 commit 6346210

File tree

4 files changed

+118
-9
lines changed

4 files changed

+118
-9
lines changed

apps/gdalalg_vector_clip.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,50 @@ class GDALVectorClipAlgorithmLayer final : public GDALVectorPipelineOutputLayer
181181

182182
} // namespace
183183

184+
/************************************************************************/
185+
/* GDALVectorClipAlgorithmLayerChangeExtent */
186+
/************************************************************************/
187+
188+
namespace
189+
{
190+
class GDALVectorClipAlgorithmLayerChangeExtent final
191+
: public GDALVectorPipelinePassthroughLayer
192+
{
193+
public:
194+
GDALVectorClipAlgorithmLayerChangeExtent(OGRLayer &oSrcLayer,
195+
const OGREnvelope &sLayerEnvelope)
196+
: GDALVectorPipelinePassthroughLayer(oSrcLayer),
197+
m_sLayerEnvelope(sLayerEnvelope)
198+
{
199+
}
200+
201+
OGRErr IGetExtent(int /*iGeomField*/, OGREnvelope *psExtent,
202+
bool /* bForce */) override
203+
{
204+
if (m_sLayerEnvelope.IsInit())
205+
{
206+
*psExtent = m_sLayerEnvelope;
207+
return OGRERR_NONE;
208+
}
209+
else
210+
{
211+
return OGRERR_FAILURE;
212+
}
213+
}
214+
215+
int TestCapability(const char *pszCap) const override
216+
{
217+
if (EQUAL(pszCap, OLCFastGetExtent))
218+
return true;
219+
return m_srcLayer.TestCapability(pszCap);
220+
}
221+
222+
private:
223+
const OGREnvelope m_sLayerEnvelope;
224+
};
225+
226+
} // namespace
227+
184228
/************************************************************************/
185229
/* GDALVectorClipAlgorithm::RunStep() */
186230
/************************************************************************/
@@ -208,6 +252,54 @@ bool GDALVectorClipAlgorithm::RunStep(GDALPipelineStepRunContext &)
208252
}
209253
}
210254

255+
if (m_bbox.empty() && m_geometry.empty() &&
256+
m_likeDataset.GetDatasetRef() == nullptr)
257+
{
258+
auto outDS =
259+
std::make_unique<GDALVectorPipelineOutputDataset>(*poSrcDS);
260+
261+
bool ret = true;
262+
for (int i = 0; ret && i < nLayerCount; ++i)
263+
{
264+
auto poSrcLayer = poSrcDS->GetLayer(i);
265+
ret = (poSrcLayer != nullptr);
266+
if (ret)
267+
{
268+
if (m_activeLayer.empty() ||
269+
m_activeLayer == poSrcLayer->GetDescription())
270+
{
271+
OGREnvelope sLayerEnvelope, sFeatureEnvelope;
272+
for (auto &&poFeature : poSrcLayer)
273+
{
274+
const auto poGeom = poFeature->GetGeometryRef();
275+
if (poGeom && !poGeom->IsEmpty())
276+
{
277+
poGeom->getEnvelope(&sFeatureEnvelope);
278+
sLayerEnvelope.Merge(sFeatureEnvelope);
279+
}
280+
}
281+
outDS->AddLayer(
282+
*poSrcLayer,
283+
std::make_unique<
284+
GDALVectorClipAlgorithmLayerChangeExtent>(
285+
*poSrcLayer, sLayerEnvelope));
286+
}
287+
else
288+
{
289+
outDS->AddLayer(
290+
*poSrcLayer,
291+
std::make_unique<GDALVectorPipelinePassthroughLayer>(
292+
*poSrcLayer));
293+
}
294+
}
295+
}
296+
297+
if (ret)
298+
m_outputDataset.Set(std::move(outDS));
299+
300+
return ret;
301+
}
302+
211303
auto [poClipGeom, errMsg] = GetClipGeometry();
212304
if (!poClipGeom)
213305
{

apps/gdalalg_vector_pipeline.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ class GDALVectorPipelinePassthroughLayer /* non final */
232232
explicit GDALVectorPipelinePassthroughLayer(OGRLayer &oSrcLayer)
233233
: GDALVectorPipelineOutputLayer(oSrcLayer)
234234
{
235+
SetDescription(oSrcLayer.GetDescription());
236+
SetMetadata(oSrcLayer.GetMetadata());
235237
}
236238

237239
const OGRFeatureDefn *GetLayerDefn() const override;

autotest/utilities/test_gdalalg_vector_clip.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -886,15 +886,28 @@ def test_gdalalg_vector_clip_like_raster_srs():
886886
assert out_lyr.GetNextFeature() is None
887887

888888

889-
def test_gdalalg_vector_clip_missing_arg(tmp_vsimem):
890-
891-
out_filename = str(tmp_vsimem / "out.shp")
889+
@pytest.mark.require_driver("GPKG")
890+
def test_gdalalg_vector_clip_no_arg(tmp_vsimem):
892891

893-
clip = get_clip_alg()
894-
with pytest.raises(
895-
Exception, match="clip: --bbox, --geometry or --like must be specified"
896-
):
897-
clip.ParseRunAndFinalize(["../ogr/data/poly.shp", out_filename])
892+
src_ds = gdal.GetDriverByName("GPKG").CreateVector(tmp_vsimem / "in.gpkg")
893+
src_lyr = src_ds.CreateLayer("test")
894+
f = ogr.Feature(src_lyr.GetLayerDefn())
895+
f.SetGeometry(ogr.CreateGeometryFromWkt("POLYGON ((0 0,0 1,1 1,1 0,0 0))"))
896+
src_lyr.CreateFeature(f)
897+
f = ogr.Feature(src_lyr.GetLayerDefn())
898+
f.SetGeometry(ogr.CreateGeometryFromWkt("POLYGON ((10 0,10 1,11 1,11 0,10 0))"))
899+
src_lyr.CreateFeature(f)
900+
src_lyr.DeleteFeature(f.GetFID())
901+
assert src_lyr.GetExtent() == (0, 11, 0, 1)
902+
src_ds.CreateLayer("empty_layer")
903+
904+
with gdal.alg.vector.clip(input=src_ds, output="", output_format="MEM") as alg:
905+
ds = alg.Output()
906+
lyr = ds.GetLayerByName("test")
907+
assert lyr.GetExtent() == (0, 1, 0, 1)
908+
assert lyr.GetFeatureCount() == 1
909+
lyr = ds.GetLayerByName("empty_layer")
910+
assert lyr.GetExtent(can_return_null=True) is None
898911

899912

900913
def test_gdalalg_vector_clip_geometry_invalid():

doc/source/programs/gdal_vector_clip.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ Description
2323
:program:`gdal vector clip` can be used to clip a vector dataset using
2424
georeferenced coordinates.
2525

26-
Either :option:`--bbox`, :option:`--geometry` or :option:`--like` must be specified.
26+
Starting with GDAL 3.13, if none of :option:`--bbox`, :option:`--geometry` or :option:`--like`
27+
is specified, the extent of output layer(s) is adjusted to fit exactly the
28+
actual extent of input layer(s).
2729

2830
``clip`` can also be used as a step of :ref:`gdal_vector_pipeline`.
2931

0 commit comments

Comments
 (0)