diff --git a/apps/gdalalg_vector_clip.cpp b/apps/gdalalg_vector_clip.cpp index ed5e9e714426..b63ad9604ebb 100644 --- a/apps/gdalalg_vector_clip.cpp +++ b/apps/gdalalg_vector_clip.cpp @@ -65,129 +65,99 @@ GDALVectorClipAlgorithm::GDALVectorClipAlgorithm(bool standaloneStep) namespace { -class GDALVectorClipAlgorithmLayer final - : public OGRLayer, - public OGRGetNextFeatureThroughRaw +class GDALVectorClipAlgorithmLayer final : public GDALVectorPipelineOutputLayer { - - DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(GDALVectorClipAlgorithmLayer) - public: - GDALVectorClipAlgorithmLayer(OGRLayer *poSrcLayer, + GDALVectorClipAlgorithmLayer(OGRLayer &oSrcLayer, std::unique_ptr poClipGeom) - : m_poSrcLayer(poSrcLayer), m_poClipGeom(std::move(poClipGeom)), - m_eSrcLayerGeomType(m_poSrcLayer->GetGeomType()), + : GDALVectorPipelineOutputLayer(oSrcLayer), + m_poClipGeom(std::move(poClipGeom)), + m_eSrcLayerGeomType(oSrcLayer.GetGeomType()), m_eFlattenSrcLayerGeomType(wkbFlatten(m_eSrcLayerGeomType)), m_bSrcLayerGeomTypeIsCollection(OGR_GT_IsSubClassOf( m_eFlattenSrcLayerGeomType, wkbGeometryCollection)) { - SetDescription(poSrcLayer->GetDescription()); - poSrcLayer->SetSpatialFilter(m_poClipGeom.get()); + SetDescription(oSrcLayer.GetDescription()); + oSrcLayer.SetSpatialFilter(m_poClipGeom.get()); } OGRFeatureDefn *GetLayerDefn() override { - return m_poSrcLayer->GetLayerDefn(); - } - - void ResetReading() override - { - m_poSrcLayer->ResetReading(); - m_poSrcFeature.reset(); - m_poCurGeomColl.reset(); - m_idxInCurGeomColl = 0; + return m_srcLayer.GetLayerDefn(); } - OGRFeature *GetNextRawFeature() + void TranslateFeature( + std::unique_ptr poSrcFeature, + std::vector> &apoOutFeatures) override { - if (m_poSrcFeature && m_poCurGeomColl) + auto poGeom = poSrcFeature->GetGeometryRef(); + if (!poGeom) + return; + + auto poIntersection = std::unique_ptr( + poGeom->Intersection(m_poClipGeom.get())); + if (!poIntersection) + return; + + const auto eFeatGeomType = + wkbFlatten(poIntersection->getGeometryType()); + if (m_eFlattenSrcLayerGeomType != wkbUnknown && + m_eFlattenSrcLayerGeomType != eFeatGeomType) { - while (m_idxInCurGeomColl < m_poCurGeomColl->getNumGeometries()) + // If the intersection is a collection of geometry and the + // layer geometry type is of non-collection type, create + // one feature per element of the collection. + if (!m_bSrcLayerGeomTypeIsCollection && + OGR_GT_IsSubClassOf(eFeatGeomType, wkbGeometryCollection)) { - const auto poGeom = - m_poCurGeomColl->getGeometryRef(m_idxInCurGeomColl); - ++m_idxInCurGeomColl; - if (m_eFlattenSrcLayerGeomType == wkbUnknown || - m_eFlattenSrcLayerGeomType == - wkbFlatten(poGeom->getGeometryType())) + auto poGeomColl = std::unique_ptr( + poIntersection.release()->toGeometryCollection()); + for (const auto *poSubGeom : poGeomColl.get()) { auto poDstFeature = - std::unique_ptr(m_poSrcFeature->Clone()); - poDstFeature->SetGeometry(poGeom); - return poDstFeature.release(); + std::unique_ptr(poSrcFeature->Clone()); + poDstFeature->SetGeometry(poSubGeom); + apoOutFeatures.push_back(std::move(poDstFeature)); } } - m_poSrcFeature.reset(); - m_poCurGeomColl.reset(); - m_idxInCurGeomColl = 0; - } - - while (auto poFeature = - std::unique_ptr(m_poSrcLayer->GetNextFeature())) - { - auto poGeom = poFeature->GetGeometryRef(); - if (!poGeom) - continue; - - auto poIntersection = std::unique_ptr( - poGeom->Intersection(m_poClipGeom.get())); - if (!poIntersection) - continue; - - const auto eFeatGeomType = - wkbFlatten(poIntersection->getGeometryType()); - if (m_eFlattenSrcLayerGeomType != wkbUnknown && - m_eFlattenSrcLayerGeomType != eFeatGeomType) + else if (OGR_GT_GetCollection(eFeatGeomType) == + m_eFlattenSrcLayerGeomType) { - // If the intersection is a collection of geometry and the - // layer geometry type is of non-collection type, create - // one feature per element of the collection. - if (!m_bSrcLayerGeomTypeIsCollection && - OGR_GT_IsSubClassOf(eFeatGeomType, wkbGeometryCollection)) - { - m_poSrcFeature = std::move(poFeature); - m_poCurGeomColl.reset( - poIntersection.release()->toGeometryCollection()); - m_idxInCurGeomColl = 0; - return GetNextFeature(); - } - else if (OGR_GT_GetCollection(eFeatGeomType) == - m_eFlattenSrcLayerGeomType) - { - poIntersection.reset(OGRGeometryFactory::forceTo( - poIntersection.release(), m_eSrcLayerGeomType)); - poFeature->SetGeometryDirectly(poIntersection.release()); - return poFeature.release(); - } - // else discard geometries of incompatible type with the - // layer geometry type + poIntersection.reset(OGRGeometryFactory::forceTo( + poIntersection.release(), m_eSrcLayerGeomType)); + poSrcFeature->SetGeometryDirectly(poIntersection.release()); + apoOutFeatures.push_back(std::move(poSrcFeature)); } - else + else if (m_eFlattenSrcLayerGeomType == wkbGeometryCollection) { - poFeature->SetGeometryDirectly(poIntersection.release()); - return poFeature.release(); + auto poGeomColl = std::make_unique(); + poGeomColl->addGeometry(std::move(poIntersection)); + poSrcFeature->SetGeometryDirectly(poGeomColl.release()); + apoOutFeatures.push_back(std::move(poSrcFeature)); } + // else discard geometries of incompatible type with the + // layer geometry type + } + else + { + poSrcFeature->SetGeometryDirectly(poIntersection.release()); + apoOutFeatures.push_back(std::move(poSrcFeature)); } - return nullptr; } int TestCapability(const char *pszCap) override { if (EQUAL(pszCap, OLCStringsAsUTF8) || EQUAL(pszCap, OLCCurveGeometries) || EQUAL(pszCap, OLCZGeometries)) - return m_poSrcLayer->TestCapability(pszCap); + return m_srcLayer.TestCapability(pszCap); return false; } private: - OGRLayer *m_poSrcLayer = nullptr; std::unique_ptr m_poClipGeom{}; const OGRwkbGeometryType m_eSrcLayerGeomType; const OGRwkbGeometryType m_eFlattenSrcLayerGeomType; const bool m_bSrcLayerGeomTypeIsCollection; - std::unique_ptr m_poSrcFeature{}; - std::unique_ptr m_poCurGeomColl{}; - int m_idxInCurGeomColl = 0; CPL_DISALLOW_COPY_ASSIGN(GDALVectorClipAlgorithmLayer) }; @@ -428,8 +398,7 @@ bool GDALVectorClipAlgorithm::RunStep(GDALProgressFunc, void *) return false; } - auto outDS = std::make_unique(); - outDS->SetDescription(poSrcDS->GetDescription()); + auto outDS = std::make_unique(*poSrcDS); bool ret = true; for (int i = 0; ret && i < nLayerCount; ++i) @@ -448,8 +417,10 @@ bool GDALVectorClipAlgorithm::RunStep(GDALProgressFunc, void *) } if (ret) { - outDS->AddLayer(std::make_unique( - poSrcLayer, std::move(poClipGeomForLayer))); + outDS->AddLayer( + *poSrcLayer, + std::make_unique( + *poSrcLayer, std::move(poClipGeomForLayer))); } } } diff --git a/apps/gdalalg_vector_pipeline.cpp b/apps/gdalalg_vector_pipeline.cpp index cade33a94b40..f7a639c36b94 100644 --- a/apps/gdalalg_vector_pipeline.cpp +++ b/apps/gdalalg_vector_pipeline.cpp @@ -516,4 +516,175 @@ std::string GDALVectorPipelineAlgorithm::GetUsageForCLI( return ret; } +/************************************************************************/ +/* GDALVectorPipelineOutputLayer */ +/************************************************************************/ + +/************************************************************************/ +/* GDALVectorPipelineOutputLayer() */ +/************************************************************************/ + +GDALVectorPipelineOutputLayer::GDALVectorPipelineOutputLayer(OGRLayer &srcLayer) + : m_srcLayer(srcLayer) +{ +} + +/************************************************************************/ +/* GDALVectorPipelineOutputLayer::ResetReading() */ +/************************************************************************/ + +void GDALVectorPipelineOutputLayer::ResetReading() +{ + m_srcLayer.ResetReading(); + m_pendingFeatures.clear(); + m_idxInPendingFeatures = 0; +} + +/************************************************************************/ +/* GDALVectorPipelineOutputLayer::GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *GDALVectorPipelineOutputLayer::GetNextRawFeature() +{ + if (m_idxInPendingFeatures < m_pendingFeatures.size()) + { + OGRFeature *poFeature = + m_pendingFeatures[m_idxInPendingFeatures].release(); + ++m_idxInPendingFeatures; + return poFeature; + } + m_pendingFeatures.clear(); + m_idxInPendingFeatures = 0; + while (true) + { + auto poSrcFeature = + std::unique_ptr(m_srcLayer.GetNextFeature()); + if (!poSrcFeature) + return nullptr; + TranslateFeature(std::move(poSrcFeature), m_pendingFeatures); + if (!m_pendingFeatures.empty()) + break; + } + OGRFeature *poFeature = m_pendingFeatures[0].release(); + m_idxInPendingFeatures = 1; + return poFeature; +} + +/************************************************************************/ +/* GDALVectorPipelineOutputDataset */ +/************************************************************************/ + +/************************************************************************/ +/* GDALVectorPipelineOutputDataset() */ +/************************************************************************/ + +GDALVectorPipelineOutputDataset::GDALVectorPipelineOutputDataset( + GDALDataset &srcDS) + : m_srcDS(srcDS) +{ + SetDescription(m_srcDS.GetDescription()); +} + +/************************************************************************/ +/* GDALVectorPipelineOutputDataset::AddLayer() */ +/************************************************************************/ + +void GDALVectorPipelineOutputDataset::AddLayer( + OGRLayer &oSrcLayer, + std::unique_ptr poNewLayer) +{ + m_layersToDestroy.push_back(std::move(poNewLayer)); + OGRLayerWithTranslateFeature *poNewLayerRaw = + m_layersToDestroy.back().get(); + m_layers.push_back(poNewLayerRaw); + m_mapSrcLayerToNewLayer[&oSrcLayer] = poNewLayerRaw; +} + +/************************************************************************/ +/* GDALVectorPipelineOutputDataset::GetLayerCount() */ +/************************************************************************/ + +int GDALVectorPipelineOutputDataset::GetLayerCount() +{ + return static_cast(m_layers.size()); +} + +/************************************************************************/ +/* GDALVectorPipelineOutputDataset::GetLayer() */ +/************************************************************************/ + +OGRLayer *GDALVectorPipelineOutputDataset::GetLayer(int idx) +{ + return idx >= 0 && idx < GetLayerCount() ? m_layers[idx] : nullptr; +} + +/************************************************************************/ +/* GDALVectorPipelineOutputDataset::TestCapability() */ +/************************************************************************/ + +int GDALVectorPipelineOutputDataset::TestCapability(const char *pszCap) +{ + if (EQUAL(pszCap, ODsCRandomLayerRead)) + { + return m_srcDS.TestCapability(pszCap); + } + return false; +} + +/************************************************************************/ +/* GDALVectorPipelineOutputDataset::ResetReading() */ +/************************************************************************/ + +void GDALVectorPipelineOutputDataset::ResetReading() +{ + m_srcDS.ResetReading(); + m_pendingFeatures.clear(); + m_idxInPendingFeatures = 0; +} + +/************************************************************************/ +/* GDALVectorPipelineOutputDataset::GetNextFeature() */ +/************************************************************************/ + +OGRFeature *GDALVectorPipelineOutputDataset::GetNextFeature( + OGRLayer **ppoBelongingLayer, double *pdfProgressPct, + GDALProgressFunc pfnProgress, void *pProgressData) +{ + if (m_idxInPendingFeatures < m_pendingFeatures.size()) + { + OGRFeature *poFeature = + m_pendingFeatures[m_idxInPendingFeatures].release(); + if (ppoBelongingLayer) + *ppoBelongingLayer = m_belongingLayer; + ++m_idxInPendingFeatures; + return poFeature; + } + + m_pendingFeatures.clear(); + m_idxInPendingFeatures = 0; + + while (true) + { + OGRLayer *poSrcBelongingLayer = nullptr; + auto poSrcFeature = std::unique_ptr(m_srcDS.GetNextFeature( + &poSrcBelongingLayer, pdfProgressPct, pfnProgress, pProgressData)); + if (!poSrcFeature) + return nullptr; + auto iterToDstLayer = m_mapSrcLayerToNewLayer.find(poSrcBelongingLayer); + if (iterToDstLayer != m_mapSrcLayerToNewLayer.end()) + { + m_belongingLayer = iterToDstLayer->second; + m_belongingLayer->TranslateFeature(std::move(poSrcFeature), + m_pendingFeatures); + if (!m_pendingFeatures.empty()) + break; + } + } + OGRFeature *poFeature = m_pendingFeatures[0].release(); + if (ppoBelongingLayer) + *ppoBelongingLayer = m_belongingLayer; + m_idxInPendingFeatures = 1; + return poFeature; +} + //! @endcond diff --git a/apps/gdalalg_vector_pipeline.h b/apps/gdalalg_vector_pipeline.h index 4bf66820e0f0..8bbf6f249f4c 100644 --- a/apps/gdalalg_vector_pipeline.h +++ b/apps/gdalalg_vector_pipeline.h @@ -17,6 +17,10 @@ #include "gdalalg_abstract_pipeline.h" #include "ogrsf_frmts.h" +#include "ogrlayerwithtranslatefeature.h" + +#include +#include //! @cond Doxygen_Suppress @@ -108,6 +112,34 @@ class GDALVectorPipelineAlgorithm final } }; +/************************************************************************/ +/* GDALVectorPipelineOutputLayer */ +/************************************************************************/ + +/** Class that implements GetNextFeature() by forwarding to + * OGRLayerWithTranslateFeature::TranslateFeature() implementation, which + * might return several features. + */ +class GDALVectorPipelineOutputLayer /* non final */ + : public OGRLayerWithTranslateFeature, + public OGRGetNextFeatureThroughRaw +{ + protected: + explicit GDALVectorPipelineOutputLayer(OGRLayer &oSrcLayer); + + DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(GDALVectorPipelineOutputLayer) + + OGRLayer &m_srcLayer; + + public: + void ResetReading() override; + OGRFeature *GetNextRawFeature(); + + private: + std::vector> m_pendingFeatures{}; + size_t m_idxInPendingFeatures = 0; +}; + /************************************************************************/ /* GDALVectorPipelineOutputDataset */ /************************************************************************/ @@ -117,32 +149,37 @@ class GDALVectorPipelineAlgorithm final */ class GDALVectorPipelineOutputDataset final : public GDALDataset { - std::vector> m_layersToDestroy{}; - std::vector m_layers{}; + GDALDataset &m_srcDS; + std::map + m_mapSrcLayerToNewLayer{}; + std::vector> + m_layersToDestroy{}; + std::vector m_layers{}; + + OGRLayerWithTranslateFeature *m_belongingLayer = nullptr; + std::vector> m_pendingFeatures{}; + size_t m_idxInPendingFeatures = 0; + + CPL_DISALLOW_COPY_ASSIGN(GDALVectorPipelineOutputDataset) public: - GDALVectorPipelineOutputDataset() = default; + explicit GDALVectorPipelineOutputDataset(GDALDataset &oSrcDS); - void AddLayer(std::unique_ptr poLayer) - { - m_layersToDestroy.push_back(std::move(poLayer)); - m_layers.push_back(m_layersToDestroy.back().get()); - } + void AddLayer(OGRLayer &oSrcLayer, + std::unique_ptr poNewLayer); - void AddLayer(OGRLayer *poLayer) - { - m_layers.push_back(poLayer); - } + int GetLayerCount() override; - int GetLayerCount() override - { - return static_cast(m_layers.size()); - } + OGRLayer *GetLayer(int idx) override; - OGRLayer *GetLayer(int idx) override - { - return idx >= 0 && idx < GetLayerCount() ? m_layers[idx] : nullptr; - } + int TestCapability(const char *pszCap) override; + + void ResetReading() override; + + OGRFeature *GetNextFeature(OGRLayer **ppoBelongingLayer, + double *pdfProgressPct, + GDALProgressFunc pfnProgress, + void *pProgressData) override; }; //! @endcond diff --git a/apps/gdalalg_vector_read.cpp b/apps/gdalalg_vector_read.cpp index 4af6c5e15d6e..a7d4aeae69c3 100644 --- a/apps/gdalalg_vector_read.cpp +++ b/apps/gdalalg_vector_read.cpp @@ -32,6 +32,121 @@ GDALVectorReadAlgorithm::GDALVectorReadAlgorithm() AddInputArgs(/* hiddenForCLI = */ false); } +/************************************************************************/ +/* GDALVectorPipelineReadOutputDataset */ +/************************************************************************/ + +/** Class used by vector pipeline steps to create an output on-the-fly + * dataset where they can store on-the-fly layers. + */ +class GDALVectorPipelineReadOutputDataset final : public GDALDataset +{ + GDALDataset &m_srcDS; + std::vector m_layers{}; + + CPL_DISALLOW_COPY_ASSIGN(GDALVectorPipelineReadOutputDataset) + + public: + explicit GDALVectorPipelineReadOutputDataset(GDALDataset &oSrcDS); + + void AddLayer(OGRLayer &oSrcLayer); + + int GetLayerCount() override; + + OGRLayer *GetLayer(int idx) override; + + int TestCapability(const char *pszCap) override; + + void ResetReading() override; + + OGRFeature *GetNextFeature(OGRLayer **ppoBelongingLayer, + double *pdfProgressPct, + GDALProgressFunc pfnProgress, + void *pProgressData) override; +}; + +/************************************************************************/ +/* GDALVectorPipelineReadOutputDataset() */ +/************************************************************************/ + +GDALVectorPipelineReadOutputDataset::GDALVectorPipelineReadOutputDataset( + GDALDataset &srcDS) + : m_srcDS(srcDS) +{ + SetDescription(m_srcDS.GetDescription()); +} + +/************************************************************************/ +/* GDALVectorPipelineReadOutputDataset::AddLayer() */ +/************************************************************************/ + +void GDALVectorPipelineReadOutputDataset::AddLayer(OGRLayer &oSrcLayer) +{ + m_layers.push_back(&oSrcLayer); +} + +/************************************************************************/ +/* GDALVectorPipelineReadOutputDataset::GetLayerCount() */ +/************************************************************************/ + +int GDALVectorPipelineReadOutputDataset::GetLayerCount() +{ + return static_cast(m_layers.size()); +} + +/************************************************************************/ +/* GDALVectorPipelineReadOutputDataset::GetLayer() */ +/************************************************************************/ + +OGRLayer *GDALVectorPipelineReadOutputDataset::GetLayer(int idx) +{ + return idx >= 0 && idx < GetLayerCount() ? m_layers[idx] : nullptr; +} + +/************************************************************************/ +/* GDALVectorPipelineReadOutputDataset::TestCapability() */ +/************************************************************************/ + +int GDALVectorPipelineReadOutputDataset::TestCapability(const char *pszCap) +{ + if (EQUAL(pszCap, ODsCRandomLayerRead)) + return m_srcDS.TestCapability(pszCap); + return false; +} + +/************************************************************************/ +/* GDALVectorPipelineReadOutputDataset::ResetReading() */ +/************************************************************************/ + +void GDALVectorPipelineReadOutputDataset::ResetReading() +{ + m_srcDS.ResetReading(); +} + +/************************************************************************/ +/* GDALVectorPipelineReadOutputDataset::GetNextFeature() */ +/************************************************************************/ + +OGRFeature *GDALVectorPipelineReadOutputDataset::GetNextFeature( + OGRLayer **ppoBelongingLayer, double *pdfProgressPct, + GDALProgressFunc pfnProgress, void *pProgressData) +{ + while (true) + { + OGRLayer *poBelongingLayer = nullptr; + auto poFeature = std::unique_ptr(m_srcDS.GetNextFeature( + &poBelongingLayer, pdfProgressPct, pfnProgress, pProgressData)); + if (ppoBelongingLayer) + *ppoBelongingLayer = poBelongingLayer; + if (!poFeature) + break; + if (std::find(m_layers.begin(), m_layers.end(), poBelongingLayer) != + m_layers.end()) + return poFeature.release(); + } + return nullptr; +} + /************************************************************************/ /* GDALVectorReadAlgorithm::RunStep() */ /************************************************************************/ @@ -49,8 +164,8 @@ bool GDALVectorReadAlgorithm::RunStep(GDALProgressFunc, void *) else { auto poSrcDS = m_inputDataset.GetDatasetRef(); - auto poOutDS = std::make_unique(); - poOutDS->SetDescription(poSrcDS->GetDescription()); + auto poOutDS = + std::make_unique(*poSrcDS); for (const auto &srcLayerName : m_inputLayerNames) { auto poSrcLayer = poSrcDS->GetLayerByName(srcLayerName.c_str()); @@ -61,7 +176,7 @@ bool GDALVectorReadAlgorithm::RunStep(GDALProgressFunc, void *) srcLayerName.c_str()); return false; } - poOutDS->AddLayer(poSrcLayer); + poOutDS->AddLayer(*poSrcLayer); } m_outputDataset.Set(std::move(poOutDS)); } diff --git a/apps/gdalalg_vector_reproject.cpp b/apps/gdalalg_vector_reproject.cpp index fccacbbe1f4b..5d23df2df4ad 100644 --- a/apps/gdalalg_vector_reproject.cpp +++ b/apps/gdalalg_vector_reproject.cpp @@ -65,8 +65,7 @@ bool GDALVectorReprojectAlgorithm::RunStep(GDALProgressFunc, void *) auto poSrcDS = m_inputDataset.GetDatasetRef(); auto reprojectedDataset = - std::make_unique(); - reprojectedDataset->SetDescription(poSrcDS->GetDescription()); + std::make_unique(*poSrcDS); const int nLayerCount = poSrcDS->GetLayerCount(); bool ret = true; @@ -95,10 +94,11 @@ bool GDALVectorReprojectAlgorithm::RunStep(GDALProgressFunc, void *) ret = (poCT != nullptr) && (poReversedCT != nullptr); if (ret) { - reprojectedDataset->AddLayer(std::make_unique( - poSrcLayer, /* iGeomField = */ 0, - /*bTakeOwnership = */ false, poCT.release(), - poReversedCT.release())); + reprojectedDataset->AddLayer( + *poSrcLayer, std::make_unique( + poSrcLayer, /* iGeomField = */ 0, + /*bTakeOwnership = */ false, + poCT.release(), poReversedCT.release())); } } } diff --git a/apps/gdalalg_vector_select.cpp b/apps/gdalalg_vector_select.cpp index 28c10de25f6e..b7a29b0599a8 100644 --- a/apps/gdalalg_vector_select.cpp +++ b/apps/gdalalg_vector_select.cpp @@ -51,7 +51,7 @@ namespace /************************************************************************/ class GDALVectorSelectAlgorithmLayer final - : public OGRLayer, + : public OGRLayerWithTranslateFeature, public OGRGetNextFeatureThroughRaw { private: @@ -257,6 +257,13 @@ class GDALVectorSelectAlgorithmLayer final m_oSrcLayer.ResetReading(); } + void TranslateFeature( + std::unique_ptr poSrcFeature, + std::vector> &apoOutFeatures) override + { + apoOutFeatures.push_back(TranslateFeature(poSrcFeature.release())); + } + OGRFeature *GetNextRawFeature() { auto poSrcFeature = @@ -304,8 +311,7 @@ bool GDALVectorSelectAlgorithm::RunStep(GDALProgressFunc, void *) auto poSrcDS = m_inputDataset.GetDatasetRef(); - auto outDS = std::make_unique(); - outDS->SetDescription(poSrcDS->GetDescription()); + auto outDS = std::make_unique(*poSrcDS); for (auto &&poSrcLayer : poSrcDS->GetLayers()) { @@ -320,7 +326,7 @@ bool GDALVectorSelectAlgorithm::RunStep(GDALProgressFunc, void *) if (!poLayer->IncludeFields(m_fields, !m_ignoreMissingFields)) return false; } - outDS->AddLayer(std::move(poLayer)); + outDS->AddLayer(*poSrcLayer, std::move(poLayer)); } m_outputDataset.Set(std::move(outDS)); diff --git a/apps/gdalalg_vector_write.cpp b/apps/gdalalg_vector_write.cpp index 3d80845862a7..7e92faaad5cd 100644 --- a/apps/gdalalg_vector_write.cpp +++ b/apps/gdalalg_vector_write.cpp @@ -43,6 +43,12 @@ bool GDALVectorWriteAlgorithm::RunStep(GDALProgressFunc pfnProgress, { CPLAssert(m_inputDataset.GetDatasetRef()); + if (m_format == "stream") + { + m_outputDataset.Set(m_inputDataset.GetDatasetRef()); + return true; + } + CPLStringList aosOptions; aosOptions.AddString("--invoked-from-gdal-vector-convert"); if (!m_overwrite) diff --git a/autotest/utilities/test_gdalalg_vector_clip.py b/autotest/utilities/test_gdalalg_vector_clip.py index 0ec6a9fea798..b0f5891adb14 100755 --- a/autotest/utilities/test_gdalalg_vector_clip.py +++ b/autotest/utilities/test_gdalalg_vector_clip.py @@ -920,3 +920,37 @@ def test_gdalalg_vector_clip_like_neither_raster_no_vector(): match="clip: Cannot get extent from clip dataset", ): clip.Run() + + +@pytest.mark.require_driver("OSM") +def test_gdalalg_vector_clip_dataset_getnextfeature(): + + clip = get_clip_alg() + src_ds = gdal.OpenEx("../ogr/data/osm/test.pbf") + clip["input"] = src_ds + clip["bbox"] = [-180, -90, 180, 90] + + assert clip.ParseCommandLineArguments( + ["--of", "stream", "--output", "streamed_output"] + ) + assert clip.Run() + + out_ds = clip["output"].GetDataset() + assert out_ds.TestCapability(ogr.ODsCRandomLayerRead) + + expected = [] + while True: + f, lyr = src_ds.GetNextFeature() + if not f: + break + expected.append((f, lyr)) + + got = [] + out_ds.ResetReading() + while True: + f, lyr = out_ds.GetNextFeature() + if not f: + break + got.append((f, lyr)) + + assert len(expected) == len(got) diff --git a/autotest/utilities/test_gdalalg_vector_pipeline.py b/autotest/utilities/test_gdalalg_vector_pipeline.py index db93707735f7..589f64946a0d 100755 --- a/autotest/utilities/test_gdalalg_vector_pipeline.py +++ b/autotest/utilities/test_gdalalg_vector_pipeline.py @@ -15,7 +15,7 @@ import pytest -from osgeo import gdal +from osgeo import gdal, ogr def get_pipeline_alg(): @@ -47,6 +47,85 @@ def my_progress(pct, msg, user_data): ) +@pytest.mark.require_driver("OSM") +def test_gdalalg_vector_pipeline_read_osm(): + + pipeline = get_pipeline_alg() + assert pipeline.ParseCommandLineArguments( + [ + "read", + "../ogr/data/osm/test.pbf", + "!", + "write", + "--of=stream", + "streamed_file", + ] + ) + assert pipeline.Run() + + out_ds = pipeline["output"].GetDataset() + assert out_ds.TestCapability(ogr.ODsCRandomLayerRead) + + expected = [] + src_ds = gdal.OpenEx("../ogr/data/osm/test.pbf") + while True: + f, _ = src_ds.GetNextFeature() + if not f: + break + expected.append(str(f)) + + got = [] + out_ds.ResetReading() + while True: + f, _ = out_ds.GetNextFeature() + if not f: + break + got.append(str(f)) + + assert expected == got + + +@pytest.mark.require_driver("OSM") +def test_gdalalg_vector_pipeline_read_osm_subset_of_layers(): + + pipeline = get_pipeline_alg() + assert pipeline.ParseCommandLineArguments( + [ + "read", + "../ogr/data/osm/test.pbf", + "--layer=points,multipolygons", + "!", + "write", + "--of=stream", + "streamed_file", + ] + ) + assert pipeline.Run() + + out_ds = pipeline["output"].GetDataset() + assert out_ds.TestCapability(ogr.ODsCRandomLayerRead) + + expected = [] + src_ds = gdal.OpenEx("../ogr/data/osm/test.pbf") + while True: + f, lyr = src_ds.GetNextFeature() + if not f: + break + if lyr.GetName() in ["points", "multipolygons"]: + expected.append(str(f)) + + got = [] + out_ds.ResetReading() + while True: + f, _ = out_ds.GetNextFeature() + if not f: + break + got.append(str(f)) + + assert len(expected) == len(got) + assert expected == got + + def test_gdalalg_vector_pipeline_pipeline_arg(tmp_vsimem): out_filename = str(tmp_vsimem / "out.shp") diff --git a/autotest/utilities/test_gdalalg_vector_reproject.py b/autotest/utilities/test_gdalalg_vector_reproject.py new file mode 100755 index 000000000000..d23faf35766d --- /dev/null +++ b/autotest/utilities/test_gdalalg_vector_reproject.py @@ -0,0 +1,57 @@ +#!/usr/bin/env pytest +# -*- coding: utf-8 -*- +############################################################################### +# Project: GDAL/OGR Test Suite +# Purpose: 'gdal vector reproject' testing +# Author: Even Rouault +# +############################################################################### +# Copyright (c) 2025, Even Rouault +# +# SPDX-License-Identifier: MIT +############################################################################### + +import pytest + +from osgeo import gdal, ogr + + +def get_reproject_alg(): + return gdal.GetGlobalAlgorithmRegistry()["vector"]["reproject"] + + +# Most of the testing is done in test_gdalalg_vector_pipeline.py + + +@pytest.mark.require_driver("OSM") +def test_gdalalg_vector_reproject_dataset_getnextfeature(): + + alg = get_reproject_alg() + src_ds = gdal.OpenEx("../ogr/data/osm/test.pbf") + alg["input"] = src_ds + alg["dst-crs"] = "EPSG:4326" + + assert alg.ParseCommandLineArguments( + ["--of", "stream", "--output", "streamed_output"] + ) + assert alg.Run() + + out_ds = alg["output"].GetDataset() + assert out_ds.TestCapability(ogr.ODsCRandomLayerRead) + + expected = [] + while True: + f, _ = src_ds.GetNextFeature() + if not f: + break + expected.append(str(f)) + + got = [] + out_ds.ResetReading() + while True: + f, _ = out_ds.GetNextFeature() + if not f: + break + got.append(str(f)) + + assert expected == got diff --git a/ogr/ogrsf_frmts/generic/CMakeLists.txt b/ogr/ogrsf_frmts/generic/CMakeLists.txt index a3ebb6142954..20b64b9cf844 100644 --- a/ogr/ogrsf_frmts/generic/CMakeLists.txt +++ b/ogr/ogrsf_frmts/generic/CMakeLists.txt @@ -37,6 +37,7 @@ add_library( ogrunionlayer.cpp ogrlayerpool.cpp ogrlayerdecorator.cpp + ogrlayerwithtranslatefeature.cpp ogreditablelayer.cpp ogrmutexeddatasource.cpp ogrmutexedlayer.cpp diff --git a/ogr/ogrsf_frmts/generic/ogrlayerdecorator.h b/ogr/ogrsf_frmts/generic/ogrlayerdecorator.h index cc56d4584490..4b619ed6d8f9 100644 --- a/ogr/ogrsf_frmts/generic/ogrlayerdecorator.h +++ b/ogr/ogrsf_frmts/generic/ogrlayerdecorator.h @@ -17,7 +17,7 @@ #include "ogrsf_frmts.h" -class CPL_DLL OGRLayerDecorator : public OGRLayer +class CPL_DLL OGRLayerDecorator : virtual public OGRLayer { CPL_DISALLOW_COPY_ASSIGN(OGRLayerDecorator) diff --git a/ogr/ogrsf_frmts/generic/ogrlayerwithtranslatefeature.cpp b/ogr/ogrsf_frmts/generic/ogrlayerwithtranslatefeature.cpp new file mode 100644 index 000000000000..2bfaf1a66e83 --- /dev/null +++ b/ogr/ogrsf_frmts/generic/ogrlayerwithtranslatefeature.cpp @@ -0,0 +1,19 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRLayerWithTranslateFeature class + * Author: Even Rouault, even dot rouault at spatialys.com + * + ****************************************************************************** + * Copyright (c) 2025, Even Rouault + * + * SPDX-License-Identifier: MIT + ****************************************************************************/ + +#include "ogrlayerwithtranslatefeature.h" + +#ifndef DOXYGEN_SKIP + +OGRLayerWithTranslateFeature::~OGRLayerWithTranslateFeature() = default; + +#endif diff --git a/ogr/ogrsf_frmts/generic/ogrlayerwithtranslatefeature.h b/ogr/ogrsf_frmts/generic/ogrlayerwithtranslatefeature.h new file mode 100644 index 000000000000..355b3774b279 --- /dev/null +++ b/ogr/ogrsf_frmts/generic/ogrlayerwithtranslatefeature.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRLayerWithTranslateFeature class + * Author: Even Rouault, even dot rouault at spatialys.com + * + ****************************************************************************** + * Copyright (c) 2025, Even Rouault + * + * SPDX-License-Identifier: MIT + ****************************************************************************/ + +#ifndef OGRLAYERWITHTRANSLATEFEATURE_H_INCLUDED +#define OGRLAYERWITHTRANSLATEFEATURE_H_INCLUDED + +#ifndef DOXYGEN_SKIP + +#include "ogrsf_frmts.h" + +/** Class that just extends by OGRLayer by mandating a pure virtual method + * TranslateFeature() to be implemented. + */ +class OGRLayerWithTranslateFeature /* non final */ : virtual public OGRLayer +{ + public: + virtual ~OGRLayerWithTranslateFeature(); + + /** Translate the source feature into one or several output features */ + virtual void TranslateFeature( + std::unique_ptr poSrcFeature, + std::vector> &apoOutFeatures) = 0; +}; + +#endif /* DOXYGEN_SKIP */ + +#endif /* OGRLAYERWITHTRANSLATEFEATURE_H_INCLUDED */ diff --git a/ogr/ogrsf_frmts/generic/ogrmutexeddatasource.cpp b/ogr/ogrsf_frmts/generic/ogrmutexeddatasource.cpp index 1d1cf3484f87..0aa84ef6f6be 100644 --- a/ogr/ogrsf_frmts/generic/ogrmutexeddatasource.cpp +++ b/ogr/ogrsf_frmts/generic/ogrmutexeddatasource.cpp @@ -160,7 +160,7 @@ void OGRMutexedDataSource::ReleaseResultSet(OGRLayer *poResultsSet) { std::map::iterator oIter = m_oReverseMapLayers.find( - cpl::down_cast(poResultsSet)); + dynamic_cast(poResultsSet)); CPLAssert(oIter != m_oReverseMapLayers.end()); delete poResultsSet; poResultsSet = oIter->second; diff --git a/ogr/ogrsf_frmts/generic/ogrwarpedlayer.cpp b/ogr/ogrsf_frmts/generic/ogrwarpedlayer.cpp index a69205500e82..28b7ed486162 100644 --- a/ogr/ogrsf_frmts/generic/ogrwarpedlayer.cpp +++ b/ogr/ogrsf_frmts/generic/ogrwarpedlayer.cpp @@ -101,6 +101,18 @@ OGRErr OGRWarpedLayer::ISetSpatialFilter(int iGeomField, } } +/************************************************************************/ +/* TranslateFeature() */ +/************************************************************************/ + +void OGRWarpedLayer::TranslateFeature( + std::unique_ptr poSrcFeature, + std::vector> &apoOutFeatures) +{ + apoOutFeatures.push_back( + SrcFeatureToWarpedFeature(std::move(poSrcFeature))); +} + /************************************************************************/ /* SrcFeatureToWarpedFeature() */ /************************************************************************/ diff --git a/ogr/ogrsf_frmts/generic/ogrwarpedlayer.h b/ogr/ogrsf_frmts/generic/ogrwarpedlayer.h index 468f7e138f11..f5fd6a634eb4 100644 --- a/ogr/ogrsf_frmts/generic/ogrwarpedlayer.h +++ b/ogr/ogrsf_frmts/generic/ogrwarpedlayer.h @@ -16,13 +16,22 @@ #ifndef DOXYGEN_SKIP #include "ogrlayerdecorator.h" +#include "ogrlayerwithtranslatefeature.h" + #include +#if defined(_MSC_VER) +#pragma warning(push) +// Silence warnings of the type warning C4250: 'OGRWarpedLayer': inherits 'OGRLayerDecorator::OGRLayerDecorator::GetMetadata' via dominance +#pragma warning(disable : 4250) +#endif + /************************************************************************/ /* OGRWarpedLayer */ /************************************************************************/ -class CPL_DLL OGRWarpedLayer : public OGRLayerDecorator +class CPL_DLL OGRWarpedLayer : public OGRLayerDecorator, + public OGRLayerWithTranslateFeature { CPL_DISALLOW_COPY_ASSIGN(OGRWarpedLayer) @@ -53,6 +62,10 @@ class CPL_DLL OGRWarpedLayer : public OGRLayerDecorator poReversedCT /* may be NULL, ownership acquired by OGRWarpedLayer */); virtual ~OGRWarpedLayer(); + void TranslateFeature( + std::unique_ptr poSrcFeature, + std::vector> &apoOutFeatures) override; + void SetExtent(double dfXMin, double dfYMin, double dfXMax, double dfYMax); virtual OGRErr ISetSpatialFilter(int iGeomField, @@ -83,6 +96,10 @@ class CPL_DLL OGRWarpedLayer : public OGRLayerDecorator CSLConstList papszOptions = nullptr) override; }; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + #endif /* #ifndef DOXYGEN_SKIP */ #endif // OGRWARPEDLAYER_H_INCLUDED