LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_grid.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 208 209 99.5 %
Date: 2025-04-20 20:32:24 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "vector grid" subcommand
       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_vector_grid.h"
      14             : #include "gdalalg_vector_grid_average.h"
      15             : #include "gdalalg_vector_grid_data_metrics.h"
      16             : #include "gdalalg_vector_grid_invdist.h"
      17             : #include "gdalalg_vector_grid_invdistnn.h"
      18             : #include "gdalalg_vector_grid_linear.h"
      19             : #include "gdalalg_vector_grid_nearest.h"
      20             : 
      21             : #include "cpl_conv.h"
      22             : #include "gdal_priv.h"
      23             : #include "gdal_utils.h"
      24             : #include "ogrsf_frmts.h"
      25             : 
      26             : //! @cond Doxygen_Suppress
      27             : 
      28             : #ifndef _
      29             : #define _(x) (x)
      30             : #endif
      31             : 
      32             : /************************************************************************/
      33             : /*            GDALVectorGridAlgorithm::GDALVectorGridAlgorithm()        */
      34             : /************************************************************************/
      35             : 
      36          92 : GDALVectorGridAlgorithm::GDALVectorGridAlgorithm()
      37          92 :     : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
      38             : {
      39          92 :     RegisterSubAlgorithm<GDALVectorGridAverageAlgorithm>();
      40          92 :     RegisterSubAlgorithm<GDALVectorGridInvdistAlgorithm>();
      41          92 :     RegisterSubAlgorithm<GDALVectorGridInvdistNNAlgorithm>();
      42          92 :     RegisterSubAlgorithm<GDALVectorGridLinearAlgorithm>();
      43          92 :     RegisterSubAlgorithm<GDALVectorGridNearestAlgorithm>();
      44          92 :     RegisterSubAlgorithm<GDALVectorGridMinimumAlgorithm>();
      45          92 :     RegisterSubAlgorithm<GDALVectorGridMaximumAlgorithm>();
      46          92 :     RegisterSubAlgorithm<GDALVectorGridRangeAlgorithm>();
      47          92 :     RegisterSubAlgorithm<GDALVectorGridCountAlgorithm>();
      48          92 :     RegisterSubAlgorithm<GDALVectorGridAverageDistanceAlgorithm>();
      49          92 :     RegisterSubAlgorithm<GDALVectorGridAverageDistancePointsAlgorithm>();
      50          92 : }
      51             : 
      52             : /************************************************************************/
      53             : /*                GDALVectorGeomAlgorithm::RunImpl()                    */
      54             : /************************************************************************/
      55             : 
      56           1 : bool GDALVectorGridAlgorithm::RunImpl(GDALProgressFunc, void *)
      57             : {
      58           1 :     CPLError(CE_Failure, CPLE_AppDefined,
      59             :              "The Run() method should not be called directly on the \"gdal "
      60             :              "vector grid\" program.");
      61           1 :     return false;
      62             : }
      63             : 
      64             : /************************************************************************/
      65             : /*                 GDALVectorGridAbstractAlgorithm()                    */
      66             : /************************************************************************/
      67             : 
      68         101 : GDALVectorGridAbstractAlgorithm::GDALVectorGridAbstractAlgorithm(
      69             :     const std::string &name, const std::string &description,
      70         101 :     const std::string &helpURL)
      71         101 :     : GDALAlgorithm(name, description, helpURL)
      72             : {
      73         101 :     AddProgressArg();
      74         101 :     AddOutputFormatArg(&m_outputFormat)
      75             :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
      76         303 :                          {GDAL_DCAP_RASTER, GDAL_DCAP_CREATE});
      77         101 :     AddOpenOptionsArg(&m_openOptions);
      78         101 :     AddInputFormatsArg(&m_inputFormats)
      79         202 :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR});
      80         101 :     AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR);
      81         101 :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
      82         101 :     AddCreationOptionsArg(&m_creationOptions);
      83             :     AddArg("extent", 0, _("Set the target georeferenced extent"),
      84         202 :            &m_targetExtent)
      85         101 :         .SetMinCount(4)
      86         101 :         .SetMaxCount(4)
      87         101 :         .SetRepeatedArgAllowed(false)
      88         101 :         .SetMetaVar("<xmin>,<ymin>,<xmax>,<ymax>");
      89         202 :     AddArg("resolution", 0, _("Set the target resolution"), &m_targetResolution)
      90         101 :         .SetMinCount(2)
      91         101 :         .SetMaxCount(2)
      92         101 :         .SetRepeatedArgAllowed(false)
      93         202 :         .SetMetaVar("<xres>,<yres>")
      94         101 :         .SetMutualExclusionGroup("size-or-resolution");
      95             :     AddArg("size", 0, _("Set the target size in pixels and lines"),
      96         202 :            &m_targetSize)
      97         101 :         .SetMinCount(2)
      98         101 :         .SetMaxCount(2)
      99         101 :         .SetRepeatedArgAllowed(false)
     100         202 :         .SetMetaVar("<xsize>,<ysize>")
     101         101 :         .SetMutualExclusionGroup("size-or-resolution");
     102         101 :     AddOutputDataTypeArg(&m_outputType).SetDefault(m_outputType);
     103         202 :     AddArg("crs", 0, _("Override the projection for the output file"), &m_crs)
     104         202 :         .AddHiddenAlias("srs")
     105         101 :         .SetIsCRSArg(/*noneAllowed=*/false);
     106         101 :     AddOverwriteArg(&m_overwrite);
     107         101 :     AddLayerNameArg(&m_layers).SetMutualExclusionGroup("layer-sql");
     108         202 :     AddArg("sql", 0, _("SQL statement"), &m_sql)
     109         101 :         .SetReadFromFileAtSyntaxAllowed()
     110         202 :         .SetMetaVar("<statement>|@<filename>")
     111         101 :         .SetRemoveSQLCommentsEnabled()
     112         101 :         .SetMutualExclusionGroup("layer-sql");
     113             :     AddBBOXArg(
     114             :         &m_bbox,
     115         101 :         _("Select only points contained within the specified bounding box"));
     116         202 :     AddArg("zfield", 0, _("Field name from which to get Z values."), &m_zField)
     117         101 :         .AddHiddenAlias("z-field");
     118             :     AddArg("zoffset", 0,
     119             :            _("Value to add to the Z field value (applied before zmultiply)"),
     120         202 :            &m_zOffset)
     121         101 :         .SetDefault(m_zOffset)
     122         101 :         .AddHiddenAlias("z-offset");
     123             :     AddArg("zmultiply", 0,
     124             :            _("Multiplication factor for the Z field value (applied after "
     125             :              "zoffset)"),
     126         202 :            &m_zMultiply)
     127         101 :         .SetDefault(m_zMultiply)
     128         101 :         .AddHiddenAlias("z-multiply");
     129             : 
     130         101 :     AddValidationAction(
     131          92 :         [this]()
     132             :         {
     133          89 :             bool ret = true;
     134          89 :             if (!m_targetResolution.empty() && m_targetExtent.empty())
     135             :             {
     136           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     137             :                             "'resolution' should be defined when 'extent' is.");
     138           1 :                 ret = false;
     139             :             }
     140          89 :             return ret;
     141             :         });
     142         101 : }
     143             : 
     144             : /************************************************************************/
     145             : /*               GDALVectorGridAbstractAlgorithm::RunImpl()             */
     146             : /************************************************************************/
     147             : 
     148          78 : bool GDALVectorGridAbstractAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     149             :                                               void *pProgressData)
     150             : {
     151          78 :     auto poSrcDS = m_inputDataset.GetDatasetRef();
     152          78 :     CPLAssert(poSrcDS);
     153          78 :     if (m_outputDataset.GetDatasetRef())
     154             :     {
     155           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     156             :                  "gdal vector grid does not support outputting to an "
     157             :                  "already opened output dataset");
     158           1 :         return false;
     159             :     }
     160             : 
     161         154 :     CPLStringList aosOptions;
     162             : 
     163          77 :     if (!m_outputFormat.empty())
     164             :     {
     165          72 :         aosOptions.AddString("-of");
     166          72 :         aosOptions.AddString(m_outputFormat.c_str());
     167             :     }
     168             : 
     169          78 :     for (const auto &co : m_creationOptions)
     170             :     {
     171           1 :         aosOptions.AddString("-co");
     172           1 :         aosOptions.AddString(co.c_str());
     173             :     }
     174             : 
     175          77 :     if (!m_targetExtent.empty())
     176             :     {
     177           6 :         aosOptions.AddString("-txe");
     178           6 :         aosOptions.AddString(CPLSPrintf("%.17g", m_targetExtent[0]));
     179           6 :         aosOptions.AddString(CPLSPrintf("%.17g", m_targetExtent[2]));
     180           6 :         aosOptions.AddString("-tye");
     181           6 :         aosOptions.AddString(CPLSPrintf("%.17g", m_targetExtent[1]));
     182           6 :         aosOptions.AddString(CPLSPrintf("%.17g", m_targetExtent[3]));
     183             :     }
     184             : 
     185          77 :     if (!m_bbox.empty())
     186             :     {
     187           2 :         aosOptions.AddString("-clipsrc");
     188          10 :         for (double v : m_bbox)
     189             :         {
     190           8 :             aosOptions.AddString(CPLSPrintf("%.17g", v));
     191             :         }
     192             :     }
     193             : 
     194          77 :     if (!m_targetResolution.empty())
     195             :     {
     196           1 :         aosOptions.AddString("-tr");
     197           3 :         for (double targetResolution : m_targetResolution)
     198             :         {
     199           2 :             aosOptions.AddString(CPLSPrintf("%.17g", targetResolution));
     200             :         }
     201             :     }
     202             : 
     203          77 :     if (!m_targetSize.empty())
     204             :     {
     205           1 :         aosOptions.AddString("-outsize");
     206           3 :         for (int targetSize : m_targetSize)
     207             :         {
     208           2 :             aosOptions.AddString(CPLSPrintf("%d", targetSize));
     209             :         }
     210             :     }
     211             : 
     212          77 :     if (!m_outputType.empty())
     213             :     {
     214          77 :         aosOptions.AddString("-ot");
     215          77 :         aosOptions.AddString(m_outputType.c_str());
     216             :     }
     217             : 
     218          77 :     if (!m_crs.empty())
     219             :     {
     220           1 :         aosOptions.AddString("-a_srs");
     221           1 :         aosOptions.AddString(m_crs.c_str());
     222             :     }
     223             : 
     224          77 :     if (m_sql.empty())
     225             :     {
     226          78 :         for (const std::string &layer : m_layers)
     227             :         {
     228           3 :             aosOptions.AddString("-l");
     229           3 :             aosOptions.AddString(layer.c_str());
     230             :         }
     231             :     }
     232             :     else
     233             :     {
     234           2 :         aosOptions.AddString("-sql");
     235           2 :         aosOptions.AddString(m_sql.c_str());
     236             :     }
     237             : 
     238          77 :     if (m_zOffset != 0)
     239             :     {
     240           1 :         aosOptions.AddString("-z_increase");
     241           1 :         aosOptions.AddString(CPLSPrintf("%.17g", m_zOffset));
     242             :     }
     243             : 
     244          77 :     if (m_zMultiply != 0)
     245             :     {
     246          77 :         aosOptions.AddString("-z_multiply");
     247          77 :         aosOptions.AddString(CPLSPrintf("%.17g", m_zMultiply));
     248             :     }
     249             : 
     250          77 :     if (!m_zField.empty())
     251             :     {
     252           1 :         aosOptions.AddString("-zfield");
     253           1 :         aosOptions.AddString(m_zField.c_str());
     254             :     }
     255          76 :     else if (m_sql.empty())
     256             :     {
     257          75 :         const auto CheckLayer = [this](OGRLayer *poLayer)
     258             :         {
     259             :             auto poFeat =
     260         146 :                 std::unique_ptr<OGRFeature>(poLayer->GetNextFeature());
     261          73 :             poLayer->ResetReading();
     262          73 :             if (poFeat)
     263             :             {
     264          73 :                 const auto poGeom = poFeat->GetGeometryRef();
     265          73 :                 if (!poGeom->Is3D())
     266             :                 {
     267           2 :                     ReportError(
     268             :                         CE_Warning, CPLE_AppDefined,
     269             :                         "At least one geometry of layer '%s' lacks a Z "
     270             :                         "component. You may need to set the 'zfield' argument",
     271           2 :                         poLayer->GetName());
     272           2 :                     return false;
     273             :                 }
     274             :             }
     275          71 :             return true;
     276          74 :         };
     277             : 
     278          74 :         if (m_layers.empty())
     279             :         {
     280         142 :             for (auto poLayer : poSrcDS->GetLayers())
     281             :             {
     282          71 :                 if (!CheckLayer(poLayer))
     283           1 :                     break;
     284             :             }
     285             :         }
     286             :         else
     287             :         {
     288           5 :             for (const std::string &layerName : m_layers)
     289             :             {
     290           3 :                 auto poLayer = poSrcDS->GetLayerByName(layerName.c_str());
     291           3 :                 if (poLayer && !CheckLayer(poLayer))
     292           1 :                     break;
     293             :             }
     294             :         }
     295             :     }
     296             : 
     297          77 :     aosOptions.AddString("-a");
     298          77 :     aosOptions.AddString(GetGridAlgorithm().c_str());
     299             : 
     300             :     std::unique_ptr<GDALGridOptions, decltype(&GDALGridOptionsFree)> psOptions{
     301         154 :         GDALGridOptionsNew(aosOptions.List(), nullptr), GDALGridOptionsFree};
     302             : 
     303          77 :     if (!psOptions)
     304             :     {
     305           0 :         return false;
     306             :     }
     307             : 
     308          77 :     GDALGridOptionsSetProgress(psOptions.get(), pfnProgress, pProgressData);
     309             : 
     310             :     VSIStatBufL sStat;
     311          77 :     std::unique_ptr<GDALDataset> poDstDS;
     312             :     const bool fileExists =
     313          77 :         VSIStatL(m_outputDataset.GetName().c_str(), &sStat) == 0;
     314             : 
     315             :     {
     316         154 :         CPLErrorStateBackuper oCPLErrorHandlerPusher(CPLQuietErrorHandler);
     317          77 :         poDstDS.reset(GDALDataset::Open(m_outputDataset.GetName().c_str(),
     318             :                                         GDAL_OF_RASTER | GDAL_OF_UPDATE,
     319             :                                         nullptr, nullptr, nullptr));
     320             :     }
     321             : 
     322          77 :     if (poDstDS)
     323             :     {
     324           3 :         if (!m_overwrite)
     325             :         {
     326           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     327             :                      "Dataset '%s' already exists. Specify the --overwrite "
     328             :                      "option to overwrite it",
     329           1 :                      m_outputDataset.GetName().c_str());
     330           1 :             return false;
     331             :         }
     332           2 :         else if (fileExists)
     333             :         {
     334           2 :             poDstDS.reset();
     335             : 
     336             :             // Delete the existing file
     337           2 :             if (VSIUnlink(m_outputDataset.GetName().c_str()) != 0)
     338             :             {
     339           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     340             :                          "Failed to delete existing dataset '%s'.",
     341           1 :                          m_outputDataset.GetName().c_str());
     342           1 :                 return false;
     343             :             }
     344             :         }
     345             :     }
     346             : 
     347             :     auto poRetDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
     348          75 :         GDALGrid(m_outputDataset.GetName().c_str(),
     349         150 :                  GDALDataset::ToHandle(poSrcDS), psOptions.get(), nullptr)));
     350             : 
     351          75 :     if (poRetDS)
     352             :     {
     353          73 :         m_outputDataset.Set(std::move(poRetDS));
     354             :     }
     355             : 
     356          75 :     return m_outputDataset.GetDatasetRef() != nullptr;
     357             : }
     358             : 
     359             : /************************************************************************/
     360             : /*            GDALVectorGridAbstractAlgorithm::AddRadiusArg()           */
     361             : /************************************************************************/
     362             : 
     363         101 : GDALInConstructionAlgorithmArg &GDALVectorGridAbstractAlgorithm::AddRadiusArg()
     364             : {
     365         202 :     return AddArg("radius", 0, _("Radius of the search circle"), &m_radius)
     366         202 :         .SetMutualExclusionGroup("radius");
     367             : }
     368             : 
     369             : /************************************************************************/
     370             : /*       GDALVectorGridAbstractAlgorithm::AddRadius1AndRadius2Arg()      */
     371             : /************************************************************************/
     372             : 
     373          85 : void GDALVectorGridAbstractAlgorithm::AddRadius1AndRadius2Arg()
     374             : {
     375         170 :     AddArg("radius1", 0, _("First axis of the search ellipse"), &m_radius1)
     376          85 :         .SetMutualExclusionGroup("radius");
     377          85 :     AddArg("radius2", 0, _("Second axis of the search ellipse"), &m_radius2);
     378             : 
     379          85 :     AddValidationAction(
     380         178 :         [this]()
     381             :         {
     382          75 :             bool ret = true;
     383          75 :             if (m_radius1 > 0 && m_radius2 == 0)
     384             :             {
     385           2 :                 ReportError(CE_Failure, CPLE_AppDefined,
     386             :                             "'radius2' should be defined when 'radius1' is.");
     387           2 :                 ret = false;
     388             :             }
     389          73 :             else if (m_radius2 > 0 && m_radius1 == 0)
     390             :             {
     391           2 :                 ReportError(CE_Failure, CPLE_AppDefined,
     392             :                             "'radius1' should be defined when 'radius2' is.");
     393           2 :                 ret = false;
     394             :             }
     395          75 :             return ret;
     396             :         });
     397          85 : }
     398             : 
     399             : /************************************************************************/
     400             : /*            GDALVectorGridAbstractAlgorithm::AddAngleArg()            */
     401             : /************************************************************************/
     402             : 
     403          85 : GDALInConstructionAlgorithmArg &GDALVectorGridAbstractAlgorithm::AddAngleArg()
     404             : {
     405             :     return AddArg("angle", 0,
     406             :                   _("Angle of search ellipse rotation in degrees (counter "
     407             :                     "clockwise)"),
     408         170 :                   &m_angle)
     409         170 :         .SetDefault(m_angle);
     410             : }
     411             : 
     412             : /************************************************************************/
     413             : /*           GDALVectorGridAbstractAlgorithm::AddMinPointsArg()         */
     414             : /************************************************************************/
     415             : 
     416             : GDALInConstructionAlgorithmArg &
     417          89 : GDALVectorGridAbstractAlgorithm::AddMinPointsArg()
     418             : {
     419             :     return AddArg("min-points", 0, _("Minimum number of data points to use"),
     420         178 :                   &m_minPoints)
     421         178 :         .SetDefault(m_minPoints);
     422             : }
     423             : 
     424             : /************************************************************************/
     425             : /*           GDALVectorGridAbstractAlgorithm::AddMaxPointsArg()         */
     426             : /************************************************************************/
     427             : 
     428             : GDALInConstructionAlgorithmArg &
     429          71 : GDALVectorGridAbstractAlgorithm::AddMaxPointsArg()
     430             : {
     431             :     return AddArg("max-points", 0, _("Maximum number of data points to use"),
     432         142 :                   &m_maxPoints)
     433         142 :         .SetDefault(m_maxPoints);
     434             : }
     435             : 
     436             : /************************************************************************/
     437             : /*    GDALVectorGridAbstractAlgorithm::AddMinMaxPointsPerQuadrantArg()  */
     438             : /************************************************************************/
     439             : 
     440          89 : void GDALVectorGridAbstractAlgorithm::AddMinMaxPointsPerQuadrantArg()
     441             : {
     442             :     AddArg("min-points-per-quadrant", 0,
     443             :            _("Minimum number of data points to use per quadrant"),
     444         178 :            &m_minPointsPerQuadrant)
     445          89 :         .SetDefault(m_minPointsPerQuadrant);
     446             :     AddArg("max-points-per-quadrant", 0,
     447             :            _("Maximum number of data points to use per quadrant"),
     448         178 :            &m_maxPointsPerQuadrant)
     449          89 :         .SetDefault(m_maxPointsPerQuadrant);
     450          89 : }
     451             : 
     452             : /************************************************************************/
     453             : /*            GDALVectorGridAbstractAlgorithm::AddNodataArg()           */
     454             : /************************************************************************/
     455             : 
     456         101 : GDALInConstructionAlgorithmArg &GDALVectorGridAbstractAlgorithm::AddNodataArg()
     457             : {
     458         202 :     return AddArg("nodata", 0, _("Target nodata value"), &m_nodata)
     459         202 :         .SetDefault(m_nodata);
     460             : }
     461             : 
     462             : //! @endcond

Generated by: LCOV version 1.14