LCOV - code coverage report
Current view: top level - apps - ogr2ogr_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3066 3804 80.6 %
Date: 2024-11-21 22:18:42 Functions: 113 141 80.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Simple client for translating between formats.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2008-2015, Even Rouault <even dot rouault at spatialys.com>
      10             :  * Copyright (c) 2015, Faza Mahamood
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "gdal_utils.h"
      17             : #include "gdal_utils_priv.h"
      18             : #include "gdalargumentparser.h"
      19             : 
      20             : #include <cassert>
      21             : #include <climits>
      22             : #include <cstdio>
      23             : #include <cstdlib>
      24             : #include <cstring>
      25             : 
      26             : #include <algorithm>
      27             : #include <atomic>
      28             : #include <future>
      29             : #include <limits>
      30             : #include <map>
      31             : #include <memory>
      32             : #include <mutex>
      33             : #include <set>
      34             : #include <unordered_set>
      35             : #include <string>
      36             : #include <utility>
      37             : #include <vector>
      38             : 
      39             : #include "commonutils.h"
      40             : #include "cpl_conv.h"
      41             : #include "cpl_error.h"
      42             : #include "cpl_progress.h"
      43             : #include "cpl_string.h"
      44             : #include "cpl_time.h"
      45             : #include "cpl_vsi.h"
      46             : #include "gdal.h"
      47             : #include "gdal_alg.h"
      48             : #include "gdal_alg_priv.h"
      49             : #include "gdal_priv.h"
      50             : #include "ogr_api.h"
      51             : #include "ogr_core.h"
      52             : #include "ogr_feature.h"
      53             : #include "ogr_featurestyle.h"
      54             : #include "ogr_geometry.h"
      55             : #include "ogr_p.h"
      56             : #include "ogr_recordbatch.h"
      57             : #include "ogr_spatialref.h"
      58             : #include "ogrlayerarrow.h"
      59             : #include "ogrlayerdecorator.h"
      60             : #include "ogrsf_frmts.h"
      61             : #include "ogr_wkb.h"
      62             : 
      63             : typedef enum
      64             : {
      65             :     GEOMOP_NONE,
      66             :     GEOMOP_SEGMENTIZE,
      67             :     GEOMOP_SIMPLIFY_PRESERVE_TOPOLOGY,
      68             : } GeomOperation;
      69             : 
      70             : typedef enum
      71             : {
      72             :     GTC_DEFAULT,
      73             :     GTC_PROMOTE_TO_MULTI,
      74             :     GTC_CONVERT_TO_LINEAR,
      75             :     GTC_CONVERT_TO_CURVE,
      76             :     GTC_PROMOTE_TO_MULTI_AND_CONVERT_TO_LINEAR,
      77             : } GeomTypeConversion;
      78             : 
      79             : #define GEOMTYPE_UNCHANGED -2
      80             : 
      81             : #define COORD_DIM_UNCHANGED -1
      82             : #define COORD_DIM_LAYER_DIM -2
      83             : #define COORD_DIM_XYM -3
      84             : 
      85             : #define TZ_OFFSET_INVALID INT_MIN
      86             : 
      87             : /************************************************************************/
      88             : /*                              CopyableGCPs                            */
      89             : /************************************************************************/
      90             : 
      91             : namespace gdal::ogr2ogr_lib
      92             : {
      93             : struct CopyableGCPs
      94             : {
      95             :     /*! size of the list pasGCPs */
      96             :     int nGCPCount = 0;
      97             : 
      98             :     /*! list of ground control points to be added */
      99             :     GDAL_GCP *pasGCPs = nullptr;
     100             : 
     101         762 :     CopyableGCPs() = default;
     102             : 
     103         717 :     CopyableGCPs(const CopyableGCPs &other)
     104         717 :     {
     105         717 :         nGCPCount = other.nGCPCount;
     106         717 :         if (other.nGCPCount)
     107           7 :             pasGCPs = GDALDuplicateGCPs(other.nGCPCount, other.pasGCPs);
     108             :         else
     109         710 :             pasGCPs = nullptr;
     110         717 :     }
     111             : 
     112        1476 :     ~CopyableGCPs()
     113        1476 :     {
     114        1476 :         if (pasGCPs)
     115             :         {
     116          14 :             GDALDeinitGCPs(nGCPCount, pasGCPs);
     117          14 :             CPLFree(pasGCPs);
     118             :         }
     119        1476 :     }
     120             : 
     121             :     CopyableGCPs &operator=(const CopyableGCPs &) = delete;
     122             : };
     123             : }  // namespace gdal::ogr2ogr_lib
     124             : 
     125             : using namespace gdal::ogr2ogr_lib;
     126             : 
     127             : /************************************************************************/
     128             : /*                        GDALVectorTranslateOptions                    */
     129             : /************************************************************************/
     130             : 
     131             : /** Options for use with GDALVectorTranslate(). GDALVectorTranslateOptions* must
     132             :  * be allocated and freed with GDALVectorTranslateOptionsNew() and
     133             :  * GDALVectorTranslateOptionsFree() respectively.
     134             :  */
     135             : struct GDALVectorTranslateOptions
     136             : {
     137             :     // All arguments passed to GDALVectorTranslate() except the positional
     138             :     // ones (that is dataset names and layer names)
     139             :     CPLStringList aosArguments{};
     140             : 
     141             :     /*! continue after a failure, skipping the failed feature */
     142             :     bool bSkipFailures = false;
     143             : 
     144             :     /*! use layer level transaction. If set to FALSE, then it is interpreted as
     145             :      * dataset level transaction. */
     146             :     int nLayerTransaction = -1;
     147             : 
     148             :     /*! force the use of particular transaction type based on
     149             :      * GDALVectorTranslate::nLayerTransaction */
     150             :     bool bForceTransaction = false;
     151             : 
     152             :     /*! group nGroupTransactions features per transaction.
     153             :        Increase the value for better performance when writing into DBMS drivers
     154             :        that have transaction support. nGroupTransactions can be set to -1 to
     155             :        load the data into a single transaction */
     156             :     int nGroupTransactions = 100 * 1000;
     157             : 
     158             :     /*! If provided, only the feature with this feature id will be reported.
     159             :        Operates exclusive of the spatial or attribute queries. Note: if you want
     160             :        to select several features based on their feature id, you can also use
     161             :        the fact the 'fid' is a special field recognized by OGR SQL. So
     162             :        GDALVectorTranslateOptions::pszWHERE = "fid in (1,3,5)" would select
     163             :        features 1, 3 and 5. */
     164             :     GIntBig nFIDToFetch = OGRNullFID;
     165             : 
     166             :     /*! allow or suppress progress monitor and other non-error output */
     167             :     bool bQuiet = false;
     168             : 
     169             :     /*! output file format name */
     170             :     std::string osFormat{};
     171             : 
     172             :     /*! list of layers of the source dataset which needs to be selected */
     173             :     CPLStringList aosLayers{};
     174             : 
     175             :     /*! dataset creation option (format specific) */
     176             :     CPLStringList aosDSCO{};
     177             : 
     178             :     /*! layer creation option (format specific) */
     179             :     CPLStringList aosLCO{};
     180             : 
     181             :     /*! access modes */
     182             :     GDALVectorTranslateAccessMode eAccessMode = ACCESS_CREATION;
     183             : 
     184             :     /*! whether to use UpsertFeature() instead of CreateFeature() */
     185             :     bool bUpsert = false;
     186             : 
     187             :     /*! It has the effect of adding, to existing target layers, the new fields
     188             :        found in source layers. This option is useful when merging files that
     189             :        have non-strictly identical structures. This might not work for output
     190             :        formats that don't support adding fields to existing non-empty layers. */
     191             :     bool bAddMissingFields = false;
     192             : 
     193             :     /*! It must be set to true to trigger reprojection, otherwise only SRS
     194             :      * assignment is done. */
     195             :     bool bTransform = false;
     196             : 
     197             :     /*! output SRS. GDALVectorTranslateOptions::bTransform must be set to true
     198             :        to trigger reprojection, otherwise only SRS assignment is done. */
     199             :     std::string osOutputSRSDef{};
     200             : 
     201             :     /*! Coordinate epoch of source SRS */
     202             :     double dfSourceCoordinateEpoch = 0;
     203             : 
     204             :     /*! Coordinate epoch of output SRS */
     205             :     double dfOutputCoordinateEpoch = 0;
     206             : 
     207             :     /*! override source SRS */
     208             :     std::string osSourceSRSDef{};
     209             : 
     210             :     /*! PROJ pipeline */
     211             :     std::string osCTPipeline{};
     212             : 
     213             :     bool bNullifyOutputSRS = false;
     214             : 
     215             :     /*! If set to false, then field name matching between source and existing
     216             :        target layer is done in a more relaxed way if the target driver has an
     217             :        implementation for it. */
     218             :     bool bExactFieldNameMatch = true;
     219             : 
     220             :     /*! an alternate name to the new layer */
     221             :     std::string osNewLayerName{};
     222             : 
     223             :     /*! attribute query (like SQL WHERE) */
     224             :     std::string osWHERE{};
     225             : 
     226             :     /*! name of the geometry field on which the spatial filter operates on. */
     227             :     std::string osGeomField{};
     228             : 
     229             :     /*! whether osGeomField is set (useful for empty strings) */
     230             :     bool bGeomFieldSet = false;
     231             : 
     232             :     /*! whether -select has been specified. This is of course true when
     233             :      * !aosSelFields.empty(), but this can also be set when an empty string
     234             :      * has been to disable fields. */
     235             :     bool bSelFieldsSet = false;
     236             : 
     237             :     /*! list of fields from input layer to copy to the new layer.
     238             :      * Geometry fields can also be specified in the list. */
     239             :     CPLStringList aosSelFields{};
     240             : 
     241             :     /*! SQL statement to execute. The resulting table/layer will be saved to the
     242             :      * output. */
     243             :     std::string osSQLStatement{};
     244             : 
     245             :     /*! SQL dialect. In some cases can be used to use (unoptimized) OGR SQL
     246             :        instead of the native SQL of an RDBMS by using "OGRSQL". The "SQLITE"
     247             :        dialect can also be used with any datasource. */
     248             :     std::string osDialect{};
     249             : 
     250             :     /*! the geometry type for the created layer */
     251             :     int eGType = GEOMTYPE_UNCHANGED;
     252             : 
     253             :     GeomTypeConversion eGeomTypeConversion = GTC_DEFAULT;
     254             : 
     255             :     /*! Geometric operation to perform */
     256             :     GeomOperation eGeomOp = GEOMOP_NONE;
     257             : 
     258             :     /*! the parameter to geometric operation */
     259             :     double dfGeomOpParam = 0;
     260             : 
     261             :     /*! Whether to run MakeValid */
     262             :     bool bMakeValid = false;
     263             : 
     264             :     /*! Whether to run OGRGeometry::IsValid */
     265             :     bool bSkipInvalidGeom = false;
     266             : 
     267             :     /*! list of field types to convert to a field of type string in the
     268             :        destination layer. Valid types are: Integer, Integer64, Real, String,
     269             :        Date, Time, DateTime, Binary, IntegerList, Integer64List, RealList,
     270             :        StringList. Special value "All" can be used to convert all fields to
     271             :        strings. This is an alternate way to using the CAST operator of OGR SQL,
     272             :        that may avoid typing a long SQL query. Note that this does not influence
     273             :        the field types used by the source driver, and is only an afterwards
     274             :         conversion. */
     275             :     CPLStringList aosFieldTypesToString{};
     276             : 
     277             :     /*! list of field types and the field type after conversion in the
     278             :        destination layer.
     279             :         ("srctype1=dsttype1","srctype2=dsttype2",...).
     280             :         Valid types are : Integer, Integer64, Real, String, Date, Time,
     281             :        DateTime, Binary, IntegerList, Integer64List, RealList, StringList. Types
     282             :        can also include subtype between parenthesis, such as Integer(Boolean),
     283             :        Real(Float32), ... Special value "All" can be used to convert all fields
     284             :        to another type. This is an alternate way to using the CAST operator of
     285             :        OGR SQL, that may avoid typing a long SQL query. This is a generalization
     286             :        of GDALVectorTranslateOptions::papszFieldTypeToString. Note that this
     287             :        does not influence the field types used by the source driver, and is only
     288             :        an afterwards conversion. */
     289             :     CPLStringList aosMapFieldType{};
     290             : 
     291             :     /*! set field width and precision to 0 */
     292             :     bool bUnsetFieldWidth = false;
     293             : 
     294             :     /*! display progress on terminal. Only works if input layers have the "fast
     295             :     feature count" capability */
     296             :     bool bDisplayProgress = false;
     297             : 
     298             :     /*! split geometries crossing the dateline meridian */
     299             :     bool bWrapDateline = false;
     300             : 
     301             :     /*! offset from dateline in degrees (default long. = +/- 10deg, geometries
     302             :     within 170deg to -170deg will be split) */
     303             :     double dfDateLineOffset = 10.0;
     304             : 
     305             :     /*! clip geometries when it is set to true */
     306             :     bool bClipSrc = false;
     307             : 
     308             :     std::shared_ptr<OGRGeometry> poClipSrc{};
     309             : 
     310             :     /*! clip datasource */
     311             :     std::string osClipSrcDS{};
     312             : 
     313             :     /*! select desired geometries using an SQL query */
     314             :     std::string osClipSrcSQL{};
     315             : 
     316             :     /*! selected named layer from the source clip datasource */
     317             :     std::string osClipSrcLayer{};
     318             : 
     319             :     /*! restrict desired geometries based on attribute query */
     320             :     std::string osClipSrcWhere{};
     321             : 
     322             :     std::shared_ptr<OGRGeometry> poClipDst{};
     323             : 
     324             :     /*! destination clip datasource */
     325             :     std::string osClipDstDS{};
     326             : 
     327             :     /*! select desired geometries using an SQL query */
     328             :     std::string osClipDstSQL{};
     329             : 
     330             :     /*! selected named layer from the destination clip datasource */
     331             :     std::string osClipDstLayer{};
     332             : 
     333             :     /*! restrict desired geometries based on attribute query */
     334             :     std::string osClipDstWhere{};
     335             : 
     336             :     /*! split fields of type StringList, RealList or IntegerList into as many
     337             :        fields of type String, Real or Integer as necessary. */
     338             :     bool bSplitListFields = false;
     339             : 
     340             :     /*! limit the number of subfields created for each split field. */
     341             :     int nMaxSplitListSubFields = -1;
     342             : 
     343             :     /*! produce one feature for each geometry in any kind of geometry collection
     344             :        in the source file */
     345             :     bool bExplodeCollections = false;
     346             : 
     347             :     /*! uses the specified field to fill the Z coordinates of geometries */
     348             :     std::string osZField{};
     349             : 
     350             :     /*! the list of field indexes to be copied from the source to the
     351             :        destination. The (n)th value specified in the list is the index of the
     352             :        field in the target layer definition in which the n(th) field of the
     353             :        source layer must be copied. Index count starts at zero. There must be
     354             :         exactly as many values in the list as the count of the fields in the
     355             :        source layer. We can use the "identity" option to specify that the fields
     356             :        should be transferred by using the same order. This option should be used
     357             :        along with the GDALVectorTranslateOptions::eAccessMode = ACCESS_APPEND
     358             :        option. */
     359             :     CPLStringList aosFieldMap{};
     360             : 
     361             :     /*! force the coordinate dimension to nCoordDim (valid values are 2 or 3).
     362             :        This affects both the layer geometry type, and feature geometries. */
     363             :     int nCoordDim = COORD_DIM_UNCHANGED;
     364             : 
     365             :     /*! destination dataset open option (format specific), only valid in update
     366             :      * mode */
     367             :     CPLStringList aosDestOpenOptions{};
     368             : 
     369             :     /*! If set to true, does not propagate not-nullable constraints to target
     370             :        layer if they exist in source layer */
     371             :     bool bForceNullable = false;
     372             : 
     373             :     /*! If set to true, for each field with a coded field domains, create a
     374             :        field that contains the description of the coded value. */
     375             :     bool bResolveDomains = false;
     376             : 
     377             :     /*! If set to true, empty string values will be treated as null */
     378             :     bool bEmptyStrAsNull = false;
     379             : 
     380             :     /*! If set to true, does not propagate default field values to target layer
     381             :        if they exist in source layer */
     382             :     bool bUnsetDefault = false;
     383             : 
     384             :     /*! to prevent the new default behavior that consists in, if the output
     385             :        driver has a FID layer creation option and we are not in append mode, to
     386             :        preserve the name of the source FID column and source feature IDs */
     387             :     bool bUnsetFid = false;
     388             : 
     389             :     /*! use the FID of the source features instead of letting the output driver
     390             :        to automatically assign a new one. If not in append mode, this behavior
     391             :        becomes the default if the output driver has a FID layer creation option.
     392             :        In which case the name of the source FID column will be used and source
     393             :        feature IDs will be attempted to be preserved. This behavior can be
     394             :         disabled by option GDALVectorTranslateOptions::bUnsetFid */
     395             :     bool bPreserveFID = false;
     396             : 
     397             :     /*! set it to false to disable copying of metadata from source dataset and
     398             :        layers into target dataset and layers, when supported by output driver.
     399             :      */
     400             :     bool bCopyMD = true;
     401             : 
     402             :     /*! list of metadata key and value to set on the output dataset, when
     403             :        supported by output driver.
     404             :         ("META-TAG1=VALUE1","META-TAG2=VALUE2") */
     405             :     CPLStringList aosMetadataOptions{};
     406             : 
     407             :     /*! override spatial filter SRS */
     408             :     std::string osSpatSRSDef{};
     409             : 
     410             :     /*! list of ground control points to be added */
     411             :     CopyableGCPs oGCPs{};
     412             : 
     413             :     /*! order of polynomial used for warping (1 to 3). The default is to select
     414             :        a polynomial order based on the number of GCPs */
     415             :     int nTransformOrder = 0;
     416             : 
     417             :     /*! spatial query extents, in the SRS of the source layer(s) (or the one
     418             :        specified with GDALVectorTranslateOptions::pszSpatSRSDef). Only features
     419             :        whose geometry intersects the extents will be selected. The geometries
     420             :        will not be clipped unless GDALVectorTranslateOptions::bClipSrc is true.
     421             :      */
     422             :     std::shared_ptr<OGRGeometry> poSpatialFilter{};
     423             : 
     424             :     /*! the progress function to use */
     425             :     GDALProgressFunc pfnProgress = nullptr;
     426             : 
     427             :     /*! pointer to the progress data variable */
     428             :     void *pProgressData = nullptr;
     429             : 
     430             :     /*! Whether layer and feature native data must be transferred. */
     431             :     bool bNativeData = true;
     432             : 
     433             :     /*! Maximum number of features, or -1 if no limit. */
     434             :     GIntBig nLimit = -1;
     435             : 
     436             :     /*! Wished offset w.r.t UTC of dateTime */
     437             :     int nTZOffsetInSec = TZ_OFFSET_INVALID;
     438             : 
     439             :     /*! Geometry X,Y coordinate resolution */
     440             :     double dfXYRes = OGRGeomCoordinatePrecision::UNKNOWN;
     441             : 
     442             :     /*! Unit of dXYRes. empty string, "m", "mm" or "deg" */
     443             :     std::string osXYResUnit{};
     444             : 
     445             :     /*! Geometry Z coordinate resolution */
     446             :     double dfZRes = OGRGeomCoordinatePrecision::UNKNOWN;
     447             : 
     448             :     /*! Unit of dfZRes. empty string, "m" or "mm" */
     449             :     std::string osZResUnit{};
     450             : 
     451             :     /*! Geometry M coordinate resolution */
     452             :     double dfMRes = OGRGeomCoordinatePrecision::UNKNOWN;
     453             : 
     454             :     /*! Whether to unset geometry coordinate precision */
     455             :     bool bUnsetCoordPrecision = false;
     456             : };
     457             : 
     458             : struct TargetLayerInfo
     459             : {
     460             :     OGRLayer *m_poSrcLayer = nullptr;
     461             :     GIntBig m_nFeaturesRead = 0;
     462             :     bool m_bPerFeatureCT = 0;
     463             :     OGRLayer *m_poDstLayer = nullptr;
     464             :     bool m_bUseWriteArrowBatch = false;
     465             : 
     466             :     struct ReprojectionInfo
     467             :     {
     468             :         std::unique_ptr<OGRCoordinateTransformation> m_poCT{};
     469             :         CPLStringList m_aosTransformOptions{};
     470             :         bool m_bCanInvalidateValidity = true;
     471             :     };
     472             : 
     473             :     std::vector<ReprojectionInfo> m_aoReprojectionInfo{};
     474             : 
     475             :     std::vector<int> m_anMap{};
     476             : 
     477             :     struct ResolvedInfo
     478             :     {
     479             :         int nSrcField;
     480             :         const OGRFieldDomain *poDomain;
     481             :     };
     482             : 
     483             :     std::map<int, ResolvedInfo> m_oMapResolved{};
     484             :     std::map<const OGRFieldDomain *, std::map<std::string, std::string>>
     485             :         m_oMapDomainToKV{};
     486             :     int m_iSrcZField = -1;
     487             :     int m_iSrcFIDField = -1;
     488             :     int m_iRequestedSrcGeomField = -1;
     489             :     bool m_bPreserveFID = false;
     490             :     const char *m_pszCTPipeline = nullptr;
     491             :     bool m_bCanAvoidSetFrom = false;
     492             :     const char *m_pszSpatSRSDef = nullptr;
     493             :     OGRGeometryH m_hSpatialFilter = nullptr;
     494             :     const char *m_pszGeomField = nullptr;
     495             :     std::vector<int> m_anDateTimeFieldIdx{};
     496             :     bool m_bSupportCurves = false;
     497             :     OGRArrowArrayStream m_sArrowArrayStream{};
     498             : };
     499             : 
     500             : struct AssociatedLayers
     501             : {
     502             :     OGRLayer *poSrcLayer = nullptr;
     503             :     std::unique_ptr<TargetLayerInfo> psInfo{};
     504             : };
     505             : 
     506             : class SetupTargetLayer
     507             : {
     508             :     bool CanUseWriteArrowBatch(OGRLayer *poSrcLayer, OGRLayer *poDstLayer,
     509             :                                bool bJustCreatedLayer,
     510             :                                const GDALVectorTranslateOptions *psOptions,
     511             :                                bool bPreserveFID, bool &bError,
     512             :                                OGRArrowArrayStream &streamSrc);
     513             : 
     514             :   public:
     515             :     GDALDataset *m_poSrcDS = nullptr;
     516             :     GDALDataset *m_poDstDS = nullptr;
     517             :     CSLConstList m_papszLCO = nullptr;
     518             :     const OGRSpatialReference *m_poUserSourceSRS = nullptr;
     519             :     const OGRSpatialReference *m_poOutputSRS = nullptr;
     520             :     bool m_bTransform = false;
     521             :     bool m_bNullifyOutputSRS = false;
     522             :     bool m_bSelFieldsSet = false;
     523             :     CSLConstList m_papszSelFields = nullptr;
     524             :     bool m_bAppend = false;
     525             :     bool m_bAddMissingFields = false;
     526             :     int m_eGType = 0;
     527             :     GeomTypeConversion m_eGeomTypeConversion = GTC_DEFAULT;
     528             :     int m_nCoordDim = 0;
     529             :     bool m_bOverwrite = false;
     530             :     CSLConstList m_papszFieldTypesToString = nullptr;
     531             :     CSLConstList m_papszMapFieldType = nullptr;
     532             :     bool m_bUnsetFieldWidth = false;
     533             :     bool m_bExplodeCollections = false;
     534             :     const char *m_pszZField = nullptr;
     535             :     CSLConstList m_papszFieldMap = nullptr;
     536             :     const char *m_pszWHERE = nullptr;
     537             :     bool m_bExactFieldNameMatch = false;
     538             :     bool m_bQuiet = false;
     539             :     bool m_bForceNullable = false;
     540             :     bool m_bResolveDomains = false;
     541             :     bool m_bUnsetDefault = false;
     542             :     bool m_bUnsetFid = false;
     543             :     bool m_bPreserveFID = false;
     544             :     bool m_bCopyMD = false;
     545             :     bool m_bNativeData = false;
     546             :     bool m_bNewDataSource = false;
     547             :     const char *m_pszCTPipeline = nullptr;
     548             : 
     549             :     std::unique_ptr<TargetLayerInfo>
     550             :     Setup(OGRLayer *poSrcLayer, const char *pszNewLayerName,
     551             :           GDALVectorTranslateOptions *psOptions, GIntBig &nTotalEventsDone);
     552             : };
     553             : 
     554             : class LayerTranslator
     555             : {
     556             :     bool TranslateArrow(TargetLayerInfo *psInfo, GIntBig nCountLayerFeatures,
     557             :                         GIntBig *pnReadFeatureCount,
     558             :                         GDALProgressFunc pfnProgress, void *pProgressArg,
     559             :                         const GDALVectorTranslateOptions *psOptions);
     560             : 
     561             :   public:
     562             :     GDALDataset *m_poSrcDS = nullptr;
     563             :     GDALDataset *m_poODS = nullptr;
     564             :     bool m_bTransform = false;
     565             :     bool m_bWrapDateline = false;
     566             :     CPLString m_osDateLineOffset{};
     567             :     const OGRSpatialReference *m_poOutputSRS = nullptr;
     568             :     bool m_bNullifyOutputSRS = false;
     569             :     const OGRSpatialReference *m_poUserSourceSRS = nullptr;
     570             :     OGRCoordinateTransformation *m_poGCPCoordTrans = nullptr;
     571             :     int m_eGType = -1;
     572             :     GeomTypeConversion m_eGeomTypeConversion = GTC_DEFAULT;
     573             :     bool m_bMakeValid = false;
     574             :     bool m_bSkipInvalidGeom = false;
     575             :     int m_nCoordDim = 0;
     576             :     GeomOperation m_eGeomOp = GEOMOP_NONE;
     577             :     double m_dfGeomOpParam = 0;
     578             : 
     579             :     OGRGeometry *m_poClipSrcOri = nullptr;
     580             :     bool m_bWarnedClipSrcSRS = false;
     581             :     std::unique_ptr<OGRGeometry> m_poClipSrcReprojectedToSrcSRS{};
     582             :     const OGRSpatialReference *m_poClipSrcReprojectedToSrcSRS_SRS = nullptr;
     583             :     OGREnvelope m_oClipSrcEnv{};
     584             : 
     585             :     OGRGeometry *m_poClipDstOri = nullptr;
     586             :     bool m_bWarnedClipDstSRS = false;
     587             :     std::unique_ptr<OGRGeometry> m_poClipDstReprojectedToDstSRS{};
     588             :     const OGRSpatialReference *m_poClipDstReprojectedToDstSRS_SRS = nullptr;
     589             :     OGREnvelope m_oClipDstEnv{};
     590             : 
     591             :     bool m_bExplodeCollections = false;
     592             :     bool m_bNativeData = false;
     593             :     GIntBig m_nLimit = -1;
     594             :     OGRGeometryFactory::TransformWithOptionsCache m_transformWithOptionsCache{};
     595             : 
     596             :     bool Translate(OGRFeature *poFeatureIn, TargetLayerInfo *psInfo,
     597             :                    GIntBig nCountLayerFeatures, GIntBig *pnReadFeatureCount,
     598             :                    GIntBig &nTotalEventsDone, GDALProgressFunc pfnProgress,
     599             :                    void *pProgressArg,
     600             :                    const GDALVectorTranslateOptions *psOptions);
     601             : 
     602             :   private:
     603             :     std::pair<const OGRGeometry *, const OGREnvelope *>
     604             :     GetDstClipGeom(const OGRSpatialReference *poGeomSRS);
     605             :     std::pair<const OGRGeometry *, const OGREnvelope *>
     606             :     GetSrcClipGeom(const OGRSpatialReference *poGeomSRS);
     607             : };
     608             : 
     609             : static OGRLayer *GetLayerAndOverwriteIfNecessary(GDALDataset *poDstDS,
     610             :                                                  const char *pszNewLayerName,
     611             :                                                  bool bOverwrite,
     612             :                                                  bool *pbErrorOccurred,
     613             :                                                  bool *pbOverwriteActuallyDone,
     614             :                                                  bool *pbAddOverwriteLCO);
     615             : 
     616             : /************************************************************************/
     617             : /*                           LoadGeometry()                             */
     618             : /************************************************************************/
     619             : 
     620          18 : static std::unique_ptr<OGRGeometry> LoadGeometry(const std::string &osDS,
     621             :                                                  const std::string &osSQL,
     622             :                                                  const std::string &osLyr,
     623             :                                                  const std::string &osWhere,
     624             :                                                  bool bMakeValid)
     625             : {
     626             :     auto poDS = std::unique_ptr<GDALDataset>(
     627          36 :         GDALDataset::Open(osDS.c_str(), GDAL_OF_VECTOR));
     628          18 :     if (poDS == nullptr)
     629           3 :         return nullptr;
     630             : 
     631          15 :     OGRLayer *poLyr = nullptr;
     632          15 :     if (!osSQL.empty())
     633           3 :         poLyr = poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
     634          12 :     else if (!osLyr.empty())
     635           2 :         poLyr = poDS->GetLayerByName(osLyr.c_str());
     636             :     else
     637          10 :         poLyr = poDS->GetLayer(0);
     638             : 
     639          15 :     if (poLyr == nullptr)
     640             :     {
     641           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     642             :                  "Failed to identify source layer from datasource.");
     643           0 :         return nullptr;
     644             :     }
     645             : 
     646          15 :     if (!osWhere.empty())
     647           5 :         poLyr->SetAttributeFilter(osWhere.c_str());
     648             : 
     649          30 :     OGRGeometryCollection oGC;
     650             : 
     651          15 :     const auto poSRSSrc = poLyr->GetSpatialRef();
     652          15 :     if (poSRSSrc)
     653             :     {
     654           2 :         auto poSRSClone = poSRSSrc->Clone();
     655           2 :         oGC.assignSpatialReference(poSRSClone);
     656           2 :         poSRSClone->Release();
     657             :     }
     658             : 
     659          30 :     for (auto &poFeat : poLyr)
     660             :     {
     661          15 :         auto poSrcGeom = std::unique_ptr<OGRGeometry>(poFeat->StealGeometry());
     662          15 :         if (poSrcGeom)
     663             :         {
     664             :             // Only take into account areal geometries.
     665          15 :             if (poSrcGeom->getDimension() == 2)
     666             :             {
     667          15 :                 if (!poSrcGeom->IsValid())
     668             :                 {
     669           4 :                     if (!bMakeValid)
     670             :                     {
     671           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
     672             :                                  "Geometry of feature " CPL_FRMT_GIB " of %s "
     673             :                                  "is invalid. You can try to make it valid by "
     674             :                                  "specifying -makevalid, but the results of "
     675             :                                  "the operation should be manually inspected.",
     676             :                                  poFeat->GetFID(), osDS.c_str());
     677           2 :                         oGC.empty();
     678           2 :                         break;
     679             :                     }
     680             :                     auto poValid =
     681           2 :                         std::unique_ptr<OGRGeometry>(poSrcGeom->MakeValid());
     682           2 :                     if (poValid)
     683             :                     {
     684           2 :                         CPLError(CE_Warning, CPLE_AppDefined,
     685             :                                  "Geometry of feature " CPL_FRMT_GIB " of %s "
     686             :                                  "was invalid and has been made valid, "
     687             :                                  "but the results of the operation "
     688             :                                  "should be manually inspected.",
     689             :                                  poFeat->GetFID(), osDS.c_str());
     690             : 
     691           2 :                         oGC.addGeometry(std::move(poValid));
     692             :                     }
     693             :                     else
     694             :                     {
     695           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     696             :                                  "Geometry of feature " CPL_FRMT_GIB " of %s "
     697             :                                  "is invalid, and could not be made valid.",
     698             :                                  poFeat->GetFID(), osDS.c_str());
     699           0 :                         oGC.empty();
     700           0 :                         break;
     701             :                     }
     702             :                 }
     703             :                 else
     704             :                 {
     705          11 :                     oGC.addGeometry(std::move(poSrcGeom));
     706             :                 }
     707             :             }
     708             :         }
     709             :     }
     710             : 
     711          15 :     if (!osSQL.empty())
     712           3 :         poDS->ReleaseResultSet(poLyr);
     713             : 
     714          15 :     if (oGC.IsEmpty())
     715           2 :         return nullptr;
     716             : 
     717          13 :     return std::unique_ptr<OGRGeometry>(oGC.UnaryUnion());
     718             : }
     719             : 
     720             : /************************************************************************/
     721             : /*                     OGRSplitListFieldLayer                           */
     722             : /************************************************************************/
     723             : 
     724             : typedef struct
     725             : {
     726             :     int iSrcIndex;
     727             :     OGRFieldType eType;
     728             :     int nMaxOccurrences;
     729             :     int nWidth;
     730             : } ListFieldDesc;
     731             : 
     732             : class OGRSplitListFieldLayer : public OGRLayer
     733             : {
     734             :     OGRLayer *poSrcLayer = nullptr;
     735             :     OGRFeatureDefn *poFeatureDefn = nullptr;
     736             :     ListFieldDesc *pasListFields = nullptr;
     737             :     int nListFieldCount = 0;
     738             :     const int nMaxSplitListSubFields;
     739             : 
     740             :     std::unique_ptr<OGRFeature>
     741             :     TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature);
     742             : 
     743             :     CPL_DISALLOW_COPY_ASSIGN(OGRSplitListFieldLayer)
     744             : 
     745             :   public:
     746             :     OGRSplitListFieldLayer(OGRLayer *poSrcLayer, int nMaxSplitListSubFields);
     747             :     virtual ~OGRSplitListFieldLayer();
     748             : 
     749             :     bool BuildLayerDefn(GDALProgressFunc pfnProgress, void *pProgressArg);
     750             : 
     751             :     virtual OGRFeature *GetNextFeature() override;
     752             :     virtual OGRFeature *GetFeature(GIntBig nFID) override;
     753             :     virtual OGRFeatureDefn *GetLayerDefn() override;
     754             : 
     755           1 :     virtual void ResetReading() override
     756             :     {
     757           1 :         poSrcLayer->ResetReading();
     758           1 :     }
     759             : 
     760           1 :     virtual int TestCapability(const char *) override
     761             :     {
     762           1 :         return FALSE;
     763             :     }
     764             : 
     765           0 :     virtual GIntBig GetFeatureCount(int bForce = TRUE) override
     766             :     {
     767           0 :         return poSrcLayer->GetFeatureCount(bForce);
     768             :     }
     769             : 
     770           1 :     virtual OGRSpatialReference *GetSpatialRef() override
     771             :     {
     772           1 :         return poSrcLayer->GetSpatialRef();
     773             :     }
     774             : 
     775           0 :     virtual OGRGeometry *GetSpatialFilter() override
     776             :     {
     777           0 :         return poSrcLayer->GetSpatialFilter();
     778             :     }
     779             : 
     780           1 :     virtual OGRStyleTable *GetStyleTable() override
     781             :     {
     782           1 :         return poSrcLayer->GetStyleTable();
     783             :     }
     784             : 
     785           0 :     virtual void SetSpatialFilter(OGRGeometry *poGeom) override
     786             :     {
     787           0 :         poSrcLayer->SetSpatialFilter(poGeom);
     788           0 :     }
     789             : 
     790           0 :     virtual void SetSpatialFilter(int iGeom, OGRGeometry *poGeom) override
     791             :     {
     792           0 :         poSrcLayer->SetSpatialFilter(iGeom, poGeom);
     793           0 :     }
     794             : 
     795           0 :     virtual void SetSpatialFilterRect(double dfMinX, double dfMinY,
     796             :                                       double dfMaxX, double dfMaxY) override
     797             :     {
     798           0 :         poSrcLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY);
     799           0 :     }
     800             : 
     801           0 :     virtual void SetSpatialFilterRect(int iGeom, double dfMinX, double dfMinY,
     802             :                                       double dfMaxX, double dfMaxY) override
     803             :     {
     804           0 :         poSrcLayer->SetSpatialFilterRect(iGeom, dfMinX, dfMinY, dfMaxX, dfMaxY);
     805           0 :     }
     806             : 
     807           0 :     virtual OGRErr SetAttributeFilter(const char *pszFilter) override
     808             :     {
     809           0 :         return poSrcLayer->SetAttributeFilter(pszFilter);
     810             :     }
     811             : };
     812             : 
     813             : /************************************************************************/
     814             : /*                    OGRSplitListFieldLayer()                          */
     815             : /************************************************************************/
     816             : 
     817           1 : OGRSplitListFieldLayer::OGRSplitListFieldLayer(OGRLayer *poSrcLayerIn,
     818           1 :                                                int nMaxSplitListSubFieldsIn)
     819             :     : poSrcLayer(poSrcLayerIn),
     820             :       nMaxSplitListSubFields(
     821           1 :           nMaxSplitListSubFieldsIn < 0 ? INT_MAX : nMaxSplitListSubFieldsIn)
     822             : {
     823           1 : }
     824             : 
     825             : /************************************************************************/
     826             : /*                   ~OGRSplitListFieldLayer()                          */
     827             : /************************************************************************/
     828             : 
     829           2 : OGRSplitListFieldLayer::~OGRSplitListFieldLayer()
     830             : {
     831           1 :     if (poFeatureDefn)
     832           1 :         poFeatureDefn->Release();
     833             : 
     834           1 :     CPLFree(pasListFields);
     835           2 : }
     836             : 
     837             : /************************************************************************/
     838             : /*                       BuildLayerDefn()                               */
     839             : /************************************************************************/
     840             : 
     841           1 : bool OGRSplitListFieldLayer::BuildLayerDefn(GDALProgressFunc pfnProgress,
     842             :                                             void *pProgressArg)
     843             : {
     844           1 :     CPLAssert(poFeatureDefn == nullptr);
     845             : 
     846           1 :     OGRFeatureDefn *poSrcFieldDefn = poSrcLayer->GetLayerDefn();
     847             : 
     848           1 :     int nSrcFields = poSrcFieldDefn->GetFieldCount();
     849           1 :     pasListFields = static_cast<ListFieldDesc *>(
     850           1 :         CPLCalloc(sizeof(ListFieldDesc), nSrcFields));
     851           1 :     nListFieldCount = 0;
     852             : 
     853             :     /* Establish the list of fields of list type */
     854           6 :     for (int i = 0; i < nSrcFields; ++i)
     855             :     {
     856           5 :         OGRFieldType eType = poSrcFieldDefn->GetFieldDefn(i)->GetType();
     857           5 :         if (eType == OFTIntegerList || eType == OFTInteger64List ||
     858           3 :             eType == OFTRealList || eType == OFTStringList)
     859             :         {
     860           3 :             pasListFields[nListFieldCount].iSrcIndex = i;
     861           3 :             pasListFields[nListFieldCount].eType = eType;
     862           3 :             if (nMaxSplitListSubFields == 1)
     863           0 :                 pasListFields[nListFieldCount].nMaxOccurrences = 1;
     864           3 :             nListFieldCount++;
     865             :         }
     866             :     }
     867             : 
     868           1 :     if (nListFieldCount == 0)
     869           0 :         return false;
     870             : 
     871             :     /* No need for full scan if the limit is 1. We just to have to create */
     872             :     /* one and a single one field */
     873           1 :     if (nMaxSplitListSubFields != 1)
     874             :     {
     875           1 :         poSrcLayer->ResetReading();
     876             : 
     877             :         const GIntBig nFeatureCount =
     878           1 :             poSrcLayer->TestCapability(OLCFastFeatureCount)
     879           1 :                 ? poSrcLayer->GetFeatureCount()
     880           1 :                 : 0;
     881           1 :         GIntBig nFeatureIndex = 0;
     882             : 
     883             :         /* Scan the whole layer to compute the maximum number of */
     884             :         /* items for each field of list type */
     885           2 :         for (auto &poSrcFeature : poSrcLayer)
     886             :         {
     887           4 :             for (int i = 0; i < nListFieldCount; ++i)
     888             :             {
     889           3 :                 int nCount = 0;
     890             :                 OGRField *psField =
     891           3 :                     poSrcFeature->GetRawFieldRef(pasListFields[i].iSrcIndex);
     892           3 :                 switch (pasListFields[i].eType)
     893             :                 {
     894           1 :                     case OFTIntegerList:
     895           1 :                         nCount = psField->IntegerList.nCount;
     896           1 :                         break;
     897           1 :                     case OFTRealList:
     898           1 :                         nCount = psField->RealList.nCount;
     899           1 :                         break;
     900           1 :                     case OFTStringList:
     901             :                     {
     902           1 :                         nCount = psField->StringList.nCount;
     903           1 :                         char **paList = psField->StringList.paList;
     904           3 :                         for (int j = 0; j < nCount; j++)
     905             :                         {
     906           2 :                             int nWidth = static_cast<int>(strlen(paList[j]));
     907           2 :                             if (nWidth > pasListFields[i].nWidth)
     908           1 :                                 pasListFields[i].nWidth = nWidth;
     909             :                         }
     910           1 :                         break;
     911             :                     }
     912           0 :                     default:
     913             :                         // cppcheck-suppress knownConditionTrueFalse
     914           0 :                         CPLAssert(false);
     915             :                         break;
     916             :                 }
     917           3 :                 if (nCount > pasListFields[i].nMaxOccurrences)
     918             :                 {
     919           3 :                     if (nCount > nMaxSplitListSubFields)
     920           0 :                         nCount = nMaxSplitListSubFields;
     921           3 :                     pasListFields[i].nMaxOccurrences = nCount;
     922             :                 }
     923             :             }
     924             : 
     925           1 :             nFeatureIndex++;
     926           1 :             if (pfnProgress != nullptr && nFeatureCount != 0)
     927           0 :                 pfnProgress(nFeatureIndex * 1.0 / nFeatureCount, "",
     928             :                             pProgressArg);
     929             :         }
     930             :     }
     931             : 
     932             :     /* Now let's build the target feature definition */
     933             : 
     934           1 :     poFeatureDefn =
     935           1 :         OGRFeatureDefn::CreateFeatureDefn(poSrcFieldDefn->GetName());
     936           1 :     poFeatureDefn->Reference();
     937           1 :     poFeatureDefn->SetGeomType(wkbNone);
     938             : 
     939           1 :     for (int i = 0; i < poSrcFieldDefn->GetGeomFieldCount(); ++i)
     940             :     {
     941           0 :         poFeatureDefn->AddGeomFieldDefn(poSrcFieldDefn->GetGeomFieldDefn(i));
     942             :     }
     943             : 
     944           1 :     int iListField = 0;
     945           6 :     for (int i = 0; i < nSrcFields; ++i)
     946             :     {
     947           5 :         const OGRFieldType eType = poSrcFieldDefn->GetFieldDefn(i)->GetType();
     948           5 :         if (eType == OFTIntegerList || eType == OFTInteger64List ||
     949           3 :             eType == OFTRealList || eType == OFTStringList)
     950             :         {
     951           3 :             const int nMaxOccurrences =
     952           3 :                 pasListFields[iListField].nMaxOccurrences;
     953           3 :             const int nWidth = pasListFields[iListField].nWidth;
     954           3 :             iListField++;
     955           3 :             if (nMaxOccurrences == 1)
     956             :             {
     957             :                 OGRFieldDefn oFieldDefn(
     958           0 :                     poSrcFieldDefn->GetFieldDefn(i)->GetNameRef(),
     959             :                     (eType == OFTIntegerList)     ? OFTInteger
     960           0 :                     : (eType == OFTInteger64List) ? OFTInteger64
     961           0 :                     : (eType == OFTRealList)      ? OFTReal
     962           0 :                                                   : OFTString);
     963           0 :                 poFeatureDefn->AddFieldDefn(&oFieldDefn);
     964             :             }
     965             :             else
     966             :             {
     967           9 :                 for (int j = 0; j < nMaxOccurrences; j++)
     968             :                 {
     969          12 :                     CPLString osFieldName;
     970             :                     osFieldName.Printf(
     971           6 :                         "%s%d", poSrcFieldDefn->GetFieldDefn(i)->GetNameRef(),
     972           6 :                         j + 1);
     973             :                     OGRFieldDefn oFieldDefn(
     974             :                         osFieldName.c_str(),
     975             :                         (eType == OFTIntegerList)     ? OFTInteger
     976           8 :                         : (eType == OFTInteger64List) ? OFTInteger64
     977           4 :                         : (eType == OFTRealList)      ? OFTReal
     978          16 :                                                       : OFTString);
     979           6 :                     oFieldDefn.SetWidth(nWidth);
     980           6 :                     poFeatureDefn->AddFieldDefn(&oFieldDefn);
     981             :                 }
     982           3 :             }
     983             :         }
     984             :         else
     985             :         {
     986           2 :             poFeatureDefn->AddFieldDefn(poSrcFieldDefn->GetFieldDefn(i));
     987             :         }
     988             :     }
     989             : 
     990           1 :     return true;
     991             : }
     992             : 
     993             : /************************************************************************/
     994             : /*                       TranslateFeature()                             */
     995             : /************************************************************************/
     996             : 
     997           2 : std::unique_ptr<OGRFeature> OGRSplitListFieldLayer::TranslateFeature(
     998             :     std::unique_ptr<OGRFeature> poSrcFeature)
     999             : {
    1000           2 :     if (poSrcFeature == nullptr)
    1001           1 :         return nullptr;
    1002           1 :     if (poFeatureDefn == nullptr)
    1003           0 :         return poSrcFeature;
    1004             : 
    1005           2 :     auto poFeature = std::make_unique<OGRFeature>(poFeatureDefn);
    1006           1 :     poFeature->SetFID(poSrcFeature->GetFID());
    1007           1 :     for (int i = 0; i < poFeature->GetGeomFieldCount(); i++)
    1008             :     {
    1009           0 :         poFeature->SetGeomFieldDirectly(i, poSrcFeature->StealGeometry(i));
    1010             :     }
    1011           1 :     poFeature->SetStyleString(poFeature->GetStyleString());
    1012             : 
    1013           1 :     OGRFeatureDefn *poSrcFieldDefn = poSrcLayer->GetLayerDefn();
    1014           1 :     int nSrcFields = poSrcFeature->GetFieldCount();
    1015           1 :     int iDstField = 0;
    1016           1 :     int iListField = 0;
    1017             : 
    1018           6 :     for (int iSrcField = 0; iSrcField < nSrcFields; ++iSrcField)
    1019             :     {
    1020             :         const OGRFieldType eType =
    1021           5 :             poSrcFieldDefn->GetFieldDefn(iSrcField)->GetType();
    1022           5 :         OGRField *psField = poSrcFeature->GetRawFieldRef(iSrcField);
    1023           5 :         switch (eType)
    1024             :         {
    1025           1 :             case OFTIntegerList:
    1026             :             {
    1027           1 :                 const int nCount = std::min(nMaxSplitListSubFields,
    1028           1 :                                             psField->IntegerList.nCount);
    1029           1 :                 int *paList = psField->IntegerList.paList;
    1030           3 :                 for (int j = 0; j < nCount; ++j)
    1031           2 :                     poFeature->SetField(iDstField + j, paList[j]);
    1032           1 :                 iDstField += pasListFields[iListField].nMaxOccurrences;
    1033           1 :                 iListField++;
    1034           1 :                 break;
    1035             :             }
    1036           0 :             case OFTInteger64List:
    1037             :             {
    1038           0 :                 const int nCount = std::min(nMaxSplitListSubFields,
    1039           0 :                                             psField->Integer64List.nCount);
    1040           0 :                 GIntBig *paList = psField->Integer64List.paList;
    1041           0 :                 for (int j = 0; j < nCount; ++j)
    1042           0 :                     poFeature->SetField(iDstField + j, paList[j]);
    1043           0 :                 iDstField += pasListFields[iListField].nMaxOccurrences;
    1044           0 :                 iListField++;
    1045           0 :                 break;
    1046             :             }
    1047           1 :             case OFTRealList:
    1048             :             {
    1049             :                 const int nCount =
    1050           1 :                     std::min(nMaxSplitListSubFields, psField->RealList.nCount);
    1051           1 :                 double *paList = psField->RealList.paList;
    1052           3 :                 for (int j = 0; j < nCount; ++j)
    1053           2 :                     poFeature->SetField(iDstField + j, paList[j]);
    1054           1 :                 iDstField += pasListFields[iListField].nMaxOccurrences;
    1055           1 :                 iListField++;
    1056           1 :                 break;
    1057             :             }
    1058           1 :             case OFTStringList:
    1059             :             {
    1060           1 :                 const int nCount = std::min(nMaxSplitListSubFields,
    1061           1 :                                             psField->StringList.nCount);
    1062           1 :                 char **paList = psField->StringList.paList;
    1063           3 :                 for (int j = 0; j < nCount; ++j)
    1064           2 :                     poFeature->SetField(iDstField + j, paList[j]);
    1065           1 :                 iDstField += pasListFields[iListField].nMaxOccurrences;
    1066           1 :                 iListField++;
    1067           1 :                 break;
    1068             :             }
    1069           2 :             default:
    1070             :             {
    1071           2 :                 poFeature->SetField(iDstField, psField);
    1072           2 :                 iDstField++;
    1073           2 :                 break;
    1074             :             }
    1075             :         }
    1076             :     }
    1077             : 
    1078           1 :     return poFeature;
    1079             : }
    1080             : 
    1081             : /************************************************************************/
    1082             : /*                       GetNextFeature()                               */
    1083             : /************************************************************************/
    1084             : 
    1085           2 : OGRFeature *OGRSplitListFieldLayer::GetNextFeature()
    1086             : {
    1087           4 :     return TranslateFeature(
    1088           4 :                std::unique_ptr<OGRFeature>(poSrcLayer->GetNextFeature()))
    1089           4 :         .release();
    1090             : }
    1091             : 
    1092             : /************************************************************************/
    1093             : /*                           GetFeature()                               */
    1094             : /************************************************************************/
    1095             : 
    1096           0 : OGRFeature *OGRSplitListFieldLayer::GetFeature(GIntBig nFID)
    1097             : {
    1098           0 :     return TranslateFeature(
    1099           0 :                std::unique_ptr<OGRFeature>(poSrcLayer->GetFeature(nFID)))
    1100           0 :         .release();
    1101             : }
    1102             : 
    1103             : /************************************************************************/
    1104             : /*                        GetLayerDefn()                                */
    1105             : /************************************************************************/
    1106             : 
    1107           3 : OGRFeatureDefn *OGRSplitListFieldLayer::GetLayerDefn()
    1108             : {
    1109           3 :     if (poFeatureDefn == nullptr)
    1110           0 :         return poSrcLayer->GetLayerDefn();
    1111           3 :     return poFeatureDefn;
    1112             : }
    1113             : 
    1114             : /************************************************************************/
    1115             : /*                            GCPCoordTransformation()                  */
    1116             : /*                                                                      */
    1117             : /*      Apply GCP Transform to points                                   */
    1118             : /************************************************************************/
    1119             : 
    1120             : class GCPCoordTransformation : public OGRCoordinateTransformation
    1121             : {
    1122           0 :     GCPCoordTransformation(const GCPCoordTransformation &other)
    1123           0 :         : hTransformArg(GDALCloneTransformer(other.hTransformArg)),
    1124           0 :           bUseTPS(other.bUseTPS), poSRS(other.poSRS)
    1125             :     {
    1126           0 :         if (poSRS)
    1127           0 :             poSRS->Reference();
    1128           0 :     }
    1129             : 
    1130             :     GCPCoordTransformation &operator=(const GCPCoordTransformation &) = delete;
    1131             : 
    1132             :   public:
    1133             :     void *hTransformArg;
    1134             :     bool bUseTPS;
    1135             :     OGRSpatialReference *poSRS;
    1136             : 
    1137           7 :     GCPCoordTransformation(int nGCPCount, const GDAL_GCP *pasGCPList,
    1138             :                            int nReqOrder, OGRSpatialReference *poSRSIn)
    1139           7 :         : hTransformArg(nullptr), bUseTPS(nReqOrder < 0), poSRS(poSRSIn)
    1140             :     {
    1141           7 :         if (nReqOrder < 0)
    1142             :         {
    1143           1 :             hTransformArg =
    1144           1 :                 GDALCreateTPSTransformer(nGCPCount, pasGCPList, FALSE);
    1145             :         }
    1146             :         else
    1147             :         {
    1148           6 :             hTransformArg = GDALCreateGCPTransformer(nGCPCount, pasGCPList,
    1149             :                                                      nReqOrder, FALSE);
    1150             :         }
    1151           7 :         if (poSRS)
    1152           2 :             poSRS->Reference();
    1153           7 :     }
    1154             : 
    1155           0 :     OGRCoordinateTransformation *Clone() const override
    1156             :     {
    1157           0 :         return new GCPCoordTransformation(*this);
    1158             :     }
    1159             : 
    1160           7 :     bool IsValid() const
    1161             :     {
    1162           7 :         return hTransformArg != nullptr;
    1163             :     }
    1164             : 
    1165          14 :     virtual ~GCPCoordTransformation()
    1166           7 :     {
    1167           7 :         if (hTransformArg != nullptr)
    1168             :         {
    1169           6 :             GDALDestroyTransformer(hTransformArg);
    1170             :         }
    1171           7 :         if (poSRS)
    1172           2 :             poSRS->Dereference();
    1173          14 :     }
    1174             : 
    1175          11 :     virtual const OGRSpatialReference *GetSourceCS() const override
    1176             :     {
    1177          11 :         return poSRS;
    1178             :     }
    1179             : 
    1180          18 :     virtual const OGRSpatialReference *GetTargetCS() const override
    1181             :     {
    1182          18 :         return poSRS;
    1183             :     }
    1184             : 
    1185          11 :     virtual int Transform(size_t nCount, double *x, double *y, double *z,
    1186             :                           double * /* t */, int *pabSuccess) override
    1187             :     {
    1188          11 :         CPLAssert(nCount <=
    1189             :                   static_cast<size_t>(std::numeric_limits<int>::max()));
    1190          11 :         if (bUseTPS)
    1191           2 :             return GDALTPSTransform(hTransformArg, FALSE,
    1192             :                                     static_cast<int>(nCount), x, y, z,
    1193           2 :                                     pabSuccess);
    1194             :         else
    1195           9 :             return GDALGCPTransform(hTransformArg, FALSE,
    1196             :                                     static_cast<int>(nCount), x, y, z,
    1197           9 :                                     pabSuccess);
    1198             :     }
    1199             : 
    1200           0 :     virtual OGRCoordinateTransformation *GetInverse() const override
    1201             :     {
    1202             :         static std::once_flag flag;
    1203           0 :         std::call_once(flag,
    1204           0 :                        []()
    1205             :                        {
    1206           0 :                            CPLDebug("OGR2OGR",
    1207             :                                     "GCPCoordTransformation::GetInverse() "
    1208             :                                     "called, but not implemented");
    1209           0 :                        });
    1210           0 :         return nullptr;
    1211             :     }
    1212             : };
    1213             : 
    1214             : /************************************************************************/
    1215             : /*                            CompositeCT                               */
    1216             : /************************************************************************/
    1217             : 
    1218             : class CompositeCT : public OGRCoordinateTransformation
    1219             : {
    1220             :     OGRCoordinateTransformation *const poCT1;
    1221             :     const bool bOwnCT1;
    1222             :     OGRCoordinateTransformation *const poCT2;
    1223             :     const bool bOwnCT2;
    1224             : 
    1225             :     // Working buffer
    1226             :     std::vector<int> m_anErrorCode{};
    1227             : 
    1228          15 :     CompositeCT(const CompositeCT &other)
    1229          15 :         : poCT1(other.poCT1 ? other.poCT1->Clone() : nullptr), bOwnCT1(true),
    1230          15 :           poCT2(other.poCT2 ? other.poCT2->Clone() : nullptr), bOwnCT2(true),
    1231          45 :           m_anErrorCode({})
    1232             :     {
    1233          15 :     }
    1234             : 
    1235             :     CompositeCT &operator=(const CompositeCT &) = delete;
    1236             : 
    1237             :   public:
    1238          40 :     CompositeCT(OGRCoordinateTransformation *poCT1In, bool bOwnCT1In,
    1239             :                 OGRCoordinateTransformation *poCT2In, bool bOwnCT2In)
    1240          40 :         : poCT1(poCT1In), bOwnCT1(bOwnCT1In), poCT2(poCT2In), bOwnCT2(bOwnCT2In)
    1241             :     {
    1242          40 :     }
    1243             : 
    1244         110 :     virtual ~CompositeCT()
    1245          55 :     {
    1246          55 :         if (bOwnCT1)
    1247          15 :             delete poCT1;
    1248          55 :         if (bOwnCT2)
    1249          50 :             delete poCT2;
    1250         110 :     }
    1251             : 
    1252          15 :     OGRCoordinateTransformation *Clone() const override
    1253             :     {
    1254          15 :         return new CompositeCT(*this);
    1255             :     }
    1256             : 
    1257         112 :     virtual const OGRSpatialReference *GetSourceCS() const override
    1258             :     {
    1259         213 :         return poCT1   ? poCT1->GetSourceCS()
    1260         101 :                : poCT2 ? poCT2->GetSourceCS()
    1261         112 :                        : nullptr;
    1262             :     }
    1263             : 
    1264         344 :     virtual const OGRSpatialReference *GetTargetCS() const override
    1265             :     {
    1266         362 :         return poCT2   ? poCT2->GetTargetCS()
    1267          18 :                : poCT1 ? poCT1->GetTargetCS()
    1268         344 :                        : nullptr;
    1269             :     }
    1270             : 
    1271          10 :     virtual bool GetEmitErrors() const override
    1272             :     {
    1273          10 :         if (poCT1)
    1274           0 :             return poCT1->GetEmitErrors();
    1275          10 :         if (poCT2)
    1276          10 :             return poCT2->GetEmitErrors();
    1277           0 :         return true;
    1278             :     }
    1279             : 
    1280          20 :     virtual void SetEmitErrors(bool bEmitErrors) override
    1281             :     {
    1282          20 :         if (poCT1)
    1283           0 :             poCT1->SetEmitErrors(bEmitErrors);
    1284          20 :         if (poCT2)
    1285          20 :             poCT2->SetEmitErrors(bEmitErrors);
    1286          20 :     }
    1287             : 
    1288         306 :     virtual int Transform(size_t nCount, double *x, double *y, double *z,
    1289             :                           double *t, int *pabSuccess) override
    1290             :     {
    1291         306 :         int nResult = TRUE;
    1292         306 :         if (poCT1)
    1293          11 :             nResult = poCT1->Transform(nCount, x, y, z, t, pabSuccess);
    1294         306 :         if (nResult && poCT2)
    1295         297 :             nResult = poCT2->Transform(nCount, x, y, z, t, pabSuccess);
    1296         306 :         return nResult;
    1297             :     }
    1298             : 
    1299       10246 :     virtual int TransformWithErrorCodes(size_t nCount, double *x, double *y,
    1300             :                                         double *z, double *t,
    1301             :                                         int *panErrorCodes) override
    1302             :     {
    1303       10246 :         if (poCT1 && poCT2 && panErrorCodes)
    1304             :         {
    1305           0 :             m_anErrorCode.resize(nCount);
    1306           0 :             int nResult = poCT1->TransformWithErrorCodes(nCount, x, y, z, t,
    1307           0 :                                                          m_anErrorCode.data());
    1308           0 :             if (nResult)
    1309           0 :                 nResult = poCT2->TransformWithErrorCodes(nCount, x, y, z, t,
    1310           0 :                                                          panErrorCodes);
    1311           0 :             for (size_t i = 0; i < nCount; ++i)
    1312             :             {
    1313           0 :                 if (m_anErrorCode[i])
    1314           0 :                     panErrorCodes[i] = m_anErrorCode[i];
    1315             :             }
    1316           0 :             return nResult;
    1317             :         }
    1318       10246 :         int nResult = TRUE;
    1319       10246 :         if (poCT1)
    1320           0 :             nResult = poCT1->TransformWithErrorCodes(nCount, x, y, z, t,
    1321           0 :                                                      panErrorCodes);
    1322       10246 :         if (nResult && poCT2)
    1323       10251 :             nResult = poCT2->TransformWithErrorCodes(nCount, x, y, z, t,
    1324       10247 :                                                      panErrorCodes);
    1325       10250 :         return nResult;
    1326             :     }
    1327             : 
    1328           5 :     virtual OGRCoordinateTransformation *GetInverse() const override
    1329             :     {
    1330           5 :         if (!poCT1 && !poCT2)
    1331           0 :             return nullptr;
    1332           5 :         if (!poCT2)
    1333           0 :             return poCT1->GetInverse();
    1334           5 :         if (!poCT1)
    1335           5 :             return poCT2->GetInverse();
    1336             :         auto poInvCT1 =
    1337           0 :             std::unique_ptr<OGRCoordinateTransformation>(poCT1->GetInverse());
    1338             :         auto poInvCT2 =
    1339           0 :             std::unique_ptr<OGRCoordinateTransformation>(poCT2->GetInverse());
    1340           0 :         if (!poInvCT1 || !poInvCT2)
    1341           0 :             return nullptr;
    1342           0 :         return std::make_unique<CompositeCT>(poInvCT2.release(), true,
    1343           0 :                                              poInvCT1.release(), true)
    1344           0 :             .release();
    1345             :     }
    1346             : };
    1347             : 
    1348             : /************************************************************************/
    1349             : /*                    AxisMappingCoordinateTransformation               */
    1350             : /************************************************************************/
    1351             : 
    1352             : class AxisMappingCoordinateTransformation : public OGRCoordinateTransformation
    1353             : {
    1354             :     bool bSwapXY = false;
    1355             : 
    1356           0 :     explicit AxisMappingCoordinateTransformation(bool bSwapXYIn)
    1357           0 :         : bSwapXY(bSwapXYIn)
    1358             :     {
    1359           0 :     }
    1360             : 
    1361             :   public:
    1362           0 :     AxisMappingCoordinateTransformation(const std::vector<int> &mappingIn,
    1363             :                                         const std::vector<int> &mappingOut)
    1364           0 :     {
    1365           0 :         if (mappingIn.size() >= 2 && mappingIn[0] == 1 && mappingIn[1] == 2 &&
    1366           0 :             mappingOut.size() >= 2 && mappingOut[0] == 2 && mappingOut[1] == 1)
    1367             :         {
    1368           0 :             bSwapXY = true;
    1369             :         }
    1370           0 :         else if (mappingIn.size() >= 2 && mappingIn[0] == 2 &&
    1371           0 :                  mappingIn[1] == 1 && mappingOut.size() >= 2 &&
    1372           0 :                  mappingOut[0] == 1 && mappingOut[1] == 2)
    1373             :         {
    1374           0 :             bSwapXY = true;
    1375             :         }
    1376             :         else
    1377             :         {
    1378           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1379             :                      "Unsupported axis transformation");
    1380             :         }
    1381           0 :     }
    1382             : 
    1383           0 :     ~AxisMappingCoordinateTransformation() override
    1384           0 :     {
    1385           0 :     }
    1386             : 
    1387           0 :     virtual OGRCoordinateTransformation *Clone() const override
    1388             :     {
    1389           0 :         return new AxisMappingCoordinateTransformation(*this);
    1390             :     }
    1391             : 
    1392           0 :     virtual const OGRSpatialReference *GetSourceCS() const override
    1393             :     {
    1394           0 :         return nullptr;
    1395             :     }
    1396             : 
    1397           0 :     virtual const OGRSpatialReference *GetTargetCS() const override
    1398             :     {
    1399           0 :         return nullptr;
    1400             :     }
    1401             : 
    1402           0 :     virtual int Transform(size_t nCount, double *x, double *y, double * /*z*/,
    1403             :                           double * /*t*/, int *pabSuccess) override
    1404             :     {
    1405           0 :         for (size_t i = 0; i < nCount; i++)
    1406             :         {
    1407           0 :             if (pabSuccess)
    1408           0 :                 pabSuccess[i] = true;
    1409           0 :             if (bSwapXY)
    1410           0 :                 std::swap(x[i], y[i]);
    1411             :         }
    1412           0 :         return true;
    1413             :     }
    1414             : 
    1415           0 :     virtual int TransformWithErrorCodes(size_t nCount, double *x, double *y,
    1416             :                                         double * /*z*/, double * /*t*/,
    1417             :                                         int *panErrorCodes) override
    1418             :     {
    1419           0 :         for (size_t i = 0; i < nCount; i++)
    1420             :         {
    1421           0 :             if (panErrorCodes)
    1422           0 :                 panErrorCodes[i] = 0;
    1423           0 :             if (bSwapXY)
    1424           0 :                 std::swap(x[i], y[i]);
    1425             :         }
    1426           0 :         return true;
    1427             :     }
    1428             : 
    1429           0 :     virtual OGRCoordinateTransformation *GetInverse() const override
    1430             :     {
    1431           0 :         return new AxisMappingCoordinateTransformation(bSwapXY);
    1432             :     }
    1433             : };
    1434             : 
    1435             : /************************************************************************/
    1436             : /*                        ApplySpatialFilter()                          */
    1437             : /************************************************************************/
    1438             : 
    1439         836 : static void ApplySpatialFilter(OGRLayer *poLayer, OGRGeometry *poSpatialFilter,
    1440             :                                const OGRSpatialReference *poSpatSRS,
    1441             :                                const char *pszGeomField,
    1442             :                                const OGRSpatialReference *poSourceSRS)
    1443             : {
    1444         836 :     if (poSpatialFilter == nullptr)
    1445         828 :         return;
    1446             : 
    1447           8 :     OGRGeometry *poSpatialFilterReprojected = nullptr;
    1448           8 :     if (poSpatSRS)
    1449             :     {
    1450           4 :         poSpatialFilterReprojected = poSpatialFilter->clone();
    1451           4 :         poSpatialFilterReprojected->assignSpatialReference(poSpatSRS);
    1452             :         const OGRSpatialReference *poSpatialFilterTargetSRS =
    1453           4 :             poSourceSRS ? poSourceSRS : poLayer->GetSpatialRef();
    1454           4 :         if (poSpatialFilterTargetSRS)
    1455             :         {
    1456             :             // When transforming the spatial filter from its spat_srs to the
    1457             :             // layer SRS, make sure to densify it sufficiently to avoid issues
    1458           4 :             constexpr double SEGMENT_DISTANCE_METRE = 10 * 1000;
    1459           4 :             if (poSpatSRS->IsGeographic())
    1460             :             {
    1461             :                 const double LENGTH_OF_ONE_DEGREE =
    1462           1 :                     poSpatSRS->GetSemiMajor(nullptr) * M_PI / 180.0;
    1463           1 :                 poSpatialFilterReprojected->segmentize(SEGMENT_DISTANCE_METRE /
    1464           1 :                                                        LENGTH_OF_ONE_DEGREE);
    1465             :             }
    1466           3 :             else if (poSpatSRS->IsProjected())
    1467             :             {
    1468           3 :                 poSpatialFilterReprojected->segmentize(
    1469             :                     SEGMENT_DISTANCE_METRE /
    1470           3 :                     poSpatSRS->GetLinearUnits(nullptr));
    1471             :             }
    1472           4 :             poSpatialFilterReprojected->transformTo(poSpatialFilterTargetSRS);
    1473             :         }
    1474             :         else
    1475           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1476             :                      "cannot determine layer SRS for %s.",
    1477           0 :                      poLayer->GetDescription());
    1478             :     }
    1479             : 
    1480           8 :     if (pszGeomField != nullptr)
    1481             :     {
    1482             :         const int iGeomField =
    1483           1 :             poLayer->GetLayerDefn()->GetGeomFieldIndex(pszGeomField);
    1484           1 :         if (iGeomField >= 0)
    1485           1 :             poLayer->SetSpatialFilter(iGeomField,
    1486             :                                       poSpatialFilterReprojected
    1487             :                                           ? poSpatialFilterReprojected
    1488           1 :                                           : poSpatialFilter);
    1489             :         else
    1490           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1491             :                      "Cannot find geometry field %s.", pszGeomField);
    1492             :     }
    1493             :     else
    1494             :     {
    1495           7 :         poLayer->SetSpatialFilter(poSpatialFilterReprojected
    1496             :                                       ? poSpatialFilterReprojected
    1497           7 :                                       : poSpatialFilter);
    1498             :     }
    1499             : 
    1500           8 :     delete poSpatialFilterReprojected;
    1501             : }
    1502             : 
    1503             : /************************************************************************/
    1504             : /*                            GetFieldType()                            */
    1505             : /************************************************************************/
    1506             : 
    1507          12 : static int GetFieldType(const char *pszArg, int *pnSubFieldType)
    1508             : {
    1509          12 :     *pnSubFieldType = OFSTNone;
    1510          12 :     const char *pszOpenParenthesis = strchr(pszArg, '(');
    1511          12 :     const int nLengthBeforeParenthesis =
    1512          12 :         pszOpenParenthesis ? static_cast<int>(pszOpenParenthesis - pszArg)
    1513          11 :                            : static_cast<int>(strlen(pszArg));
    1514          72 :     for (int iType = 0; iType <= static_cast<int>(OFTMaxType); iType++)
    1515             :     {
    1516             :         const char *pszFieldTypeName =
    1517          72 :             OGRFieldDefn::GetFieldTypeName(static_cast<OGRFieldType>(iType));
    1518          72 :         if (EQUALN(pszArg, pszFieldTypeName, nLengthBeforeParenthesis) &&
    1519          12 :             pszFieldTypeName[nLengthBeforeParenthesis] == '\0')
    1520             :         {
    1521          12 :             if (pszOpenParenthesis != nullptr)
    1522             :             {
    1523           1 :                 *pnSubFieldType = -1;
    1524           2 :                 CPLString osArgSubType = pszOpenParenthesis + 1;
    1525           1 :                 if (!osArgSubType.empty() && osArgSubType.back() == ')')
    1526           1 :                     osArgSubType.pop_back();
    1527           2 :                 for (int iSubType = 0;
    1528           2 :                      iSubType <= static_cast<int>(OFSTMaxSubType); iSubType++)
    1529             :                 {
    1530             :                     const char *pszFieldSubTypeName =
    1531           2 :                         OGRFieldDefn::GetFieldSubTypeName(
    1532             :                             static_cast<OGRFieldSubType>(iSubType));
    1533           2 :                     if (EQUAL(pszFieldSubTypeName, osArgSubType))
    1534             :                     {
    1535           1 :                         *pnSubFieldType = iSubType;
    1536           1 :                         break;
    1537             :                     }
    1538             :                 }
    1539             :             }
    1540          12 :             return iType;
    1541             :         }
    1542             :     }
    1543           0 :     return -1;
    1544             : }
    1545             : 
    1546             : /************************************************************************/
    1547             : /*                           IsFieldType()                              */
    1548             : /************************************************************************/
    1549             : 
    1550           8 : static bool IsFieldType(const char *pszArg)
    1551             : {
    1552             :     int iSubType;
    1553           8 :     return GetFieldType(pszArg, &iSubType) >= 0 && iSubType >= 0;
    1554             : }
    1555             : 
    1556             : class GDALVectorTranslateWrappedDataset : public GDALDataset
    1557             : {
    1558             :     std::unique_ptr<GDALDriver> m_poDriverToFree{};
    1559             :     GDALDataset *m_poBase = nullptr;
    1560             :     OGRSpatialReference *m_poOutputSRS = nullptr;
    1561             :     const bool m_bTransform = false;
    1562             : 
    1563             :     std::vector<std::unique_ptr<OGRLayer>> m_apoLayers{};
    1564             :     std::vector<std::unique_ptr<OGRLayer>> m_apoHiddenLayers{};
    1565             : 
    1566             :     GDALVectorTranslateWrappedDataset(GDALDataset *poBase,
    1567             :                                       OGRSpatialReference *poOutputSRS,
    1568             :                                       bool bTransform);
    1569             : 
    1570             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorTranslateWrappedDataset)
    1571             : 
    1572             :   public:
    1573           3 :     virtual int GetLayerCount() override
    1574             :     {
    1575           3 :         return static_cast<int>(m_apoLayers.size());
    1576             :     }
    1577             : 
    1578             :     virtual OGRLayer *GetLayer(int nIdx) override;
    1579             :     virtual OGRLayer *GetLayerByName(const char *pszName) override;
    1580             : 
    1581             :     virtual OGRLayer *ExecuteSQL(const char *pszStatement,
    1582             :                                  OGRGeometry *poSpatialFilter,
    1583             :                                  const char *pszDialect) override;
    1584             :     virtual void ReleaseResultSet(OGRLayer *poResultsSet) override;
    1585             : 
    1586             :     static std::unique_ptr<GDALVectorTranslateWrappedDataset>
    1587             :     New(GDALDataset *poBase, OGRSpatialReference *poOutputSRS, bool bTransform);
    1588             : };
    1589             : 
    1590             : class GDALVectorTranslateWrappedLayer : public OGRLayerDecorator
    1591             : {
    1592             :     std::vector<std::unique_ptr<OGRCoordinateTransformation>> m_apoCT{};
    1593             :     OGRFeatureDefn *m_poFDefn = nullptr;
    1594             : 
    1595             :     GDALVectorTranslateWrappedLayer(OGRLayer *poBaseLayer, bool bOwnBaseLayer);
    1596             :     std::unique_ptr<OGRFeature>
    1597             :     TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeat);
    1598             : 
    1599             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorTranslateWrappedLayer)
    1600             : 
    1601             :   public:
    1602             :     virtual ~GDALVectorTranslateWrappedLayer();
    1603             : 
    1604         379 :     virtual OGRFeatureDefn *GetLayerDefn() override
    1605             :     {
    1606         379 :         return m_poFDefn;
    1607             :     }
    1608             : 
    1609             :     virtual OGRFeature *GetNextFeature() override;
    1610             :     virtual OGRFeature *GetFeature(GIntBig nFID) override;
    1611             : 
    1612             :     static std::unique_ptr<GDALVectorTranslateWrappedLayer>
    1613             :     New(OGRLayer *poBaseLayer, bool bOwnBaseLayer,
    1614             :         OGRSpatialReference *poOutputSRS, bool bTransform);
    1615             : };
    1616             : 
    1617          89 : GDALVectorTranslateWrappedLayer::GDALVectorTranslateWrappedLayer(
    1618          89 :     OGRLayer *poBaseLayer, bool bOwnBaseLayer)
    1619             :     : OGRLayerDecorator(poBaseLayer, bOwnBaseLayer),
    1620          89 :       m_apoCT(poBaseLayer->GetLayerDefn()->GetGeomFieldCount())
    1621             : {
    1622          89 : }
    1623             : 
    1624             : std::unique_ptr<GDALVectorTranslateWrappedLayer>
    1625          89 : GDALVectorTranslateWrappedLayer::New(OGRLayer *poBaseLayer, bool bOwnBaseLayer,
    1626             :                                      OGRSpatialReference *poOutputSRS,
    1627             :                                      bool bTransform)
    1628             : {
    1629             :     auto poNew = std::unique_ptr<GDALVectorTranslateWrappedLayer>(
    1630         178 :         new GDALVectorTranslateWrappedLayer(poBaseLayer, bOwnBaseLayer));
    1631          89 :     poNew->m_poFDefn = poBaseLayer->GetLayerDefn()->Clone();
    1632          89 :     poNew->m_poFDefn->Reference();
    1633          89 :     if (!poOutputSRS)
    1634           0 :         return poNew;
    1635             : 
    1636         104 :     for (int i = 0; i < poNew->m_poFDefn->GetGeomFieldCount(); i++)
    1637             :     {
    1638          15 :         if (bTransform)
    1639             :         {
    1640           0 :             const OGRSpatialReference *poSourceSRS = poBaseLayer->GetLayerDefn()
    1641           0 :                                                          ->GetGeomFieldDefn(i)
    1642           0 :                                                          ->GetSpatialRef();
    1643           0 :             if (poSourceSRS == nullptr)
    1644             :             {
    1645           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1646             :                          "Layer %s has no source SRS for geometry field %s",
    1647           0 :                          poBaseLayer->GetName(),
    1648           0 :                          poBaseLayer->GetLayerDefn()
    1649           0 :                              ->GetGeomFieldDefn(i)
    1650             :                              ->GetNameRef());
    1651           0 :                 return nullptr;
    1652             :             }
    1653             :             else
    1654             :             {
    1655           0 :                 poNew->m_apoCT[i] =
    1656           0 :                     std::unique_ptr<OGRCoordinateTransformation>(
    1657             :                         OGRCreateCoordinateTransformation(poSourceSRS,
    1658           0 :                                                           poOutputSRS));
    1659           0 :                 if (poNew->m_apoCT[i] == nullptr)
    1660             :                 {
    1661           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1662             :                              "Failed to create coordinate transformation "
    1663             :                              "between the\n"
    1664             :                              "following coordinate systems.  This may be "
    1665             :                              "because they\n"
    1666             :                              "are not transformable.");
    1667             : 
    1668           0 :                     char *pszWKT = nullptr;
    1669           0 :                     poSourceSRS->exportToPrettyWkt(&pszWKT, FALSE);
    1670           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Source:\n%s",
    1671             :                              pszWKT);
    1672           0 :                     CPLFree(pszWKT);
    1673             : 
    1674           0 :                     poOutputSRS->exportToPrettyWkt(&pszWKT, FALSE);
    1675           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Target:\n%s",
    1676             :                              pszWKT);
    1677           0 :                     CPLFree(pszWKT);
    1678             : 
    1679           0 :                     return nullptr;
    1680             :                 }
    1681             :             }
    1682             :         }
    1683          15 :         poNew->m_poFDefn->GetGeomFieldDefn(i)->SetSpatialRef(poOutputSRS);
    1684             :     }
    1685             : 
    1686          89 :     return poNew;
    1687             : }
    1688             : 
    1689         178 : GDALVectorTranslateWrappedLayer::~GDALVectorTranslateWrappedLayer()
    1690             : {
    1691          89 :     if (m_poFDefn)
    1692          89 :         m_poFDefn->Release();
    1693         178 : }
    1694             : 
    1695         661 : OGRFeature *GDALVectorTranslateWrappedLayer::GetNextFeature()
    1696             : {
    1697        1322 :     return TranslateFeature(
    1698        1322 :                std::unique_ptr<OGRFeature>(OGRLayerDecorator::GetNextFeature()))
    1699        1322 :         .release();
    1700             : }
    1701             : 
    1702           0 : OGRFeature *GDALVectorTranslateWrappedLayer::GetFeature(GIntBig nFID)
    1703             : {
    1704           0 :     return TranslateFeature(
    1705           0 :                std::unique_ptr<OGRFeature>(OGRLayerDecorator::GetFeature(nFID)))
    1706           0 :         .release();
    1707             : }
    1708             : 
    1709         661 : std::unique_ptr<OGRFeature> GDALVectorTranslateWrappedLayer::TranslateFeature(
    1710             :     std::unique_ptr<OGRFeature> poSrcFeat)
    1711             : {
    1712         661 :     if (poSrcFeat == nullptr)
    1713         103 :         return nullptr;
    1714        1116 :     auto poNewFeat = std::make_unique<OGRFeature>(m_poFDefn);
    1715         558 :     poNewFeat->SetFrom(poSrcFeat.get());
    1716         558 :     poNewFeat->SetFID(poSrcFeat->GetFID());
    1717         573 :     for (int i = 0; i < poNewFeat->GetGeomFieldCount(); i++)
    1718             :     {
    1719          15 :         OGRGeometry *poGeom = poNewFeat->GetGeomFieldRef(i);
    1720          15 :         if (poGeom)
    1721             :         {
    1722          13 :             if (m_apoCT[i])
    1723           0 :                 poGeom->transform(m_apoCT[i].get());
    1724          13 :             poGeom->assignSpatialReference(
    1725          13 :                 m_poFDefn->GetGeomFieldDefn(i)->GetSpatialRef());
    1726             :         }
    1727             :     }
    1728         558 :     return poNewFeat;
    1729             : }
    1730             : 
    1731           3 : GDALVectorTranslateWrappedDataset::GDALVectorTranslateWrappedDataset(
    1732           3 :     GDALDataset *poBase, OGRSpatialReference *poOutputSRS, bool bTransform)
    1733           3 :     : m_poBase(poBase), m_poOutputSRS(poOutputSRS), m_bTransform(bTransform)
    1734             : {
    1735           3 :     SetDescription(poBase->GetDescription());
    1736           3 :     if (poBase->GetDriver())
    1737             :     {
    1738           3 :         poDriver = new GDALDriver();
    1739           3 :         poDriver->SetDescription(poBase->GetDriver()->GetDescription());
    1740           3 :         m_poDriverToFree.reset(poDriver);
    1741             :     }
    1742           3 : }
    1743             : 
    1744             : std::unique_ptr<GDALVectorTranslateWrappedDataset>
    1745           3 : GDALVectorTranslateWrappedDataset::New(GDALDataset *poBase,
    1746             :                                        OGRSpatialReference *poOutputSRS,
    1747             :                                        bool bTransform)
    1748             : {
    1749             :     auto poNew = std::unique_ptr<GDALVectorTranslateWrappedDataset>(
    1750           6 :         new GDALVectorTranslateWrappedDataset(poBase, poOutputSRS, bTransform));
    1751          70 :     for (int i = 0; i < poBase->GetLayerCount(); i++)
    1752             :     {
    1753             :         auto poLayer = GDALVectorTranslateWrappedLayer::New(
    1754          67 :             poBase->GetLayer(i), /* bOwnBaseLayer = */ false, poOutputSRS,
    1755          67 :             bTransform);
    1756          67 :         if (poLayer == nullptr)
    1757             :         {
    1758           0 :             return nullptr;
    1759             :         }
    1760          67 :         poNew->m_apoLayers.push_back(std::move(poLayer));
    1761             :     }
    1762           3 :     return poNew;
    1763             : }
    1764             : 
    1765           0 : OGRLayer *GDALVectorTranslateWrappedDataset::GetLayer(int i)
    1766             : {
    1767           0 :     if (i < 0 || i >= static_cast<int>(m_apoLayers.size()))
    1768           0 :         return nullptr;
    1769           0 :     return m_apoLayers[i].get();
    1770             : }
    1771             : 
    1772          68 : OGRLayer *GDALVectorTranslateWrappedDataset::GetLayerByName(const char *pszName)
    1773             : {
    1774        1008 :     for (const auto &poLayer : m_apoLayers)
    1775             :     {
    1776        1008 :         if (strcmp(poLayer->GetName(), pszName) == 0)
    1777          68 :             return poLayer.get();
    1778             :     }
    1779           0 :     for (const auto &poLayer : m_apoHiddenLayers)
    1780             :     {
    1781           0 :         if (strcmp(poLayer->GetName(), pszName) == 0)
    1782           0 :             return poLayer.get();
    1783             :     }
    1784           0 :     for (const auto &poLayer : m_apoLayers)
    1785             :     {
    1786           0 :         if (EQUAL(poLayer->GetName(), pszName))
    1787           0 :             return poLayer.get();
    1788             :     }
    1789           0 :     for (const auto &poLayer : m_apoHiddenLayers)
    1790             :     {
    1791           0 :         if (EQUAL(poLayer->GetName(), pszName))
    1792           0 :             return poLayer.get();
    1793             :     }
    1794             : 
    1795           0 :     OGRLayer *poLayer = m_poBase->GetLayerByName(pszName);
    1796           0 :     if (poLayer == nullptr)
    1797           0 :         return nullptr;
    1798             : 
    1799             :     auto poNewLayer = GDALVectorTranslateWrappedLayer::New(
    1800           0 :         poLayer, /* bOwnBaseLayer = */ false, m_poOutputSRS, m_bTransform);
    1801           0 :     if (poNewLayer == nullptr)
    1802           0 :         return nullptr;
    1803             : 
    1804             :     // Replicate source dataset behavior: if the fact of calling
    1805             :     // GetLayerByName() on a initially hidden layer makes it visible through
    1806             :     // GetLayerCount()/GetLayer(), do the same. Otherwise we are going to
    1807             :     // maintain it hidden as well.
    1808           0 :     for (int i = 0; i < m_poBase->GetLayerCount(); i++)
    1809             :     {
    1810           0 :         if (m_poBase->GetLayer(i) == poLayer)
    1811             :         {
    1812           0 :             m_apoLayers.push_back(std::move(poNewLayer));
    1813           0 :             return m_apoLayers.back().get();
    1814             :         }
    1815             :     }
    1816           0 :     m_apoHiddenLayers.push_back(std::move(poNewLayer));
    1817           0 :     return m_apoHiddenLayers.back().get();
    1818             : }
    1819             : 
    1820             : OGRLayer *
    1821          22 : GDALVectorTranslateWrappedDataset::ExecuteSQL(const char *pszStatement,
    1822             :                                               OGRGeometry *poSpatialFilter,
    1823             :                                               const char *pszDialect)
    1824             : {
    1825             :     OGRLayer *poLayer =
    1826          22 :         m_poBase->ExecuteSQL(pszStatement, poSpatialFilter, pszDialect);
    1827          22 :     if (poLayer == nullptr)
    1828           0 :         return nullptr;
    1829          22 :     return GDALVectorTranslateWrappedLayer::New(
    1830          22 :                poLayer, /* bOwnBaseLayer = */ true, m_poOutputSRS, m_bTransform)
    1831          22 :         .release();
    1832             : }
    1833             : 
    1834          22 : void GDALVectorTranslateWrappedDataset::ReleaseResultSet(OGRLayer *poResultsSet)
    1835             : {
    1836          22 :     delete poResultsSet;
    1837          22 : }
    1838             : 
    1839             : /************************************************************************/
    1840             : /*                     OGR2OGRSpatialReferenceHolder                    */
    1841             : /************************************************************************/
    1842             : 
    1843             : class OGR2OGRSpatialReferenceHolder
    1844             : {
    1845             :     OGRSpatialReference *m_poSRS = nullptr;
    1846             : 
    1847             :     CPL_DISALLOW_COPY_ASSIGN(OGR2OGRSpatialReferenceHolder)
    1848             : 
    1849             :   public:
    1850             :     OGR2OGRSpatialReferenceHolder() = default;
    1851             : 
    1852         701 :     ~OGR2OGRSpatialReferenceHolder()
    1853         701 :     {
    1854         701 :         if (m_poSRS)
    1855         145 :             m_poSRS->Release();
    1856         701 :     }
    1857             : 
    1858         145 :     void assignNoRefIncrease(OGRSpatialReference *poSRS)
    1859             :     {
    1860         145 :         CPLAssert(m_poSRS == nullptr);
    1861         145 :         m_poSRS = poSRS;
    1862         145 :     }
    1863             : 
    1864        1800 :     OGRSpatialReference *get()
    1865             :     {
    1866        1800 :         return m_poSRS;
    1867             :     }
    1868             : };
    1869             : 
    1870             : /************************************************************************/
    1871             : /*                     GDALVectorTranslateCreateCopy()                  */
    1872             : /************************************************************************/
    1873             : 
    1874             : static GDALDataset *
    1875          22 : GDALVectorTranslateCreateCopy(GDALDriver *poDriver, const char *pszDest,
    1876             :                               GDALDataset *poDS,
    1877             :                               const GDALVectorTranslateOptions *psOptions)
    1878             : {
    1879          22 :     const char *const szErrorMsg = "%s not supported by this output driver";
    1880             : 
    1881          22 :     if (psOptions->bSkipFailures)
    1882             :     {
    1883           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-skipfailures");
    1884           0 :         return nullptr;
    1885             :     }
    1886          22 :     if (psOptions->nLayerTransaction >= 0)
    1887             :     {
    1888           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg,
    1889             :                  "-lyr_transaction or -ds_transaction");
    1890           0 :         return nullptr;
    1891             :     }
    1892          22 :     if (psOptions->nFIDToFetch >= 0)
    1893             :     {
    1894           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-fid");
    1895           0 :         return nullptr;
    1896             :     }
    1897          22 :     if (!psOptions->aosLCO.empty())
    1898             :     {
    1899           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-lco");
    1900           0 :         return nullptr;
    1901             :     }
    1902          22 :     if (psOptions->bAddMissingFields)
    1903             :     {
    1904           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-addfields");
    1905           0 :         return nullptr;
    1906             :     }
    1907          22 :     if (!psOptions->osSourceSRSDef.empty())
    1908             :     {
    1909           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-s_srs");
    1910           0 :         return nullptr;
    1911             :     }
    1912          22 :     if (!psOptions->bExactFieldNameMatch)
    1913             :     {
    1914           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg,
    1915             :                  "-relaxedFieldNameMatch");
    1916           0 :         return nullptr;
    1917             :     }
    1918          22 :     if (!psOptions->osNewLayerName.empty())
    1919             :     {
    1920           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-nln");
    1921           0 :         return nullptr;
    1922             :     }
    1923          22 :     if (psOptions->bSelFieldsSet)
    1924             :     {
    1925           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-select");
    1926           0 :         return nullptr;
    1927             :     }
    1928          22 :     if (!psOptions->osSQLStatement.empty())
    1929             :     {
    1930           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-sql");
    1931           0 :         return nullptr;
    1932             :     }
    1933          22 :     if (!psOptions->osDialect.empty())
    1934             :     {
    1935           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-dialect");
    1936           0 :         return nullptr;
    1937             :     }
    1938          22 :     if (psOptions->eGType != GEOMTYPE_UNCHANGED ||
    1939          22 :         psOptions->eGeomTypeConversion != GTC_DEFAULT)
    1940             :     {
    1941           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-nlt");
    1942           0 :         return nullptr;
    1943             :     }
    1944          22 :     if (!psOptions->aosFieldTypesToString.empty())
    1945             :     {
    1946           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg,
    1947             :                  "-fieldTypeToString");
    1948           0 :         return nullptr;
    1949             :     }
    1950          22 :     if (!psOptions->aosMapFieldType.empty())
    1951             :     {
    1952           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-mapFieldType");
    1953           0 :         return nullptr;
    1954             :     }
    1955          22 :     if (psOptions->bUnsetFieldWidth)
    1956             :     {
    1957           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-unsetFieldWidth");
    1958           0 :         return nullptr;
    1959             :     }
    1960          22 :     if (psOptions->bWrapDateline)
    1961             :     {
    1962           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-wrapdateline");
    1963           0 :         return nullptr;
    1964             :     }
    1965          22 :     if (psOptions->bClipSrc)
    1966             :     {
    1967           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-clipsrc");
    1968           0 :         return nullptr;
    1969             :     }
    1970          22 :     if (!psOptions->osClipSrcSQL.empty())
    1971             :     {
    1972           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-clipsrcsql");
    1973           0 :         return nullptr;
    1974             :     }
    1975          22 :     if (!psOptions->osClipSrcLayer.empty())
    1976             :     {
    1977           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-clipsrclayer");
    1978           0 :         return nullptr;
    1979             :     }
    1980          22 :     if (!psOptions->osClipSrcWhere.empty())
    1981             :     {
    1982           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-clipsrcwhere");
    1983           0 :         return nullptr;
    1984             :     }
    1985          22 :     if (!psOptions->osClipDstDS.empty() || psOptions->poClipDst)
    1986             :     {
    1987           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-clipdst");
    1988           0 :         return nullptr;
    1989             :     }
    1990          22 :     if (!psOptions->osClipDstSQL.empty())
    1991             :     {
    1992           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-clipdstsql");
    1993           0 :         return nullptr;
    1994             :     }
    1995          22 :     if (!psOptions->osClipDstLayer.empty())
    1996             :     {
    1997           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-clipdstlayer");
    1998           0 :         return nullptr;
    1999             :     }
    2000          22 :     if (!psOptions->osClipDstWhere.empty())
    2001             :     {
    2002           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-clipdstwhere");
    2003           0 :         return nullptr;
    2004             :     }
    2005          22 :     if (psOptions->bSplitListFields)
    2006             :     {
    2007           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-splitlistfields");
    2008           0 :         return nullptr;
    2009             :     }
    2010          22 :     if (psOptions->nMaxSplitListSubFields >= 0)
    2011             :     {
    2012           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-maxsubfields");
    2013           0 :         return nullptr;
    2014             :     }
    2015          22 :     if (psOptions->bExplodeCollections)
    2016             :     {
    2017           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg,
    2018             :                  "-explodecollections");
    2019           0 :         return nullptr;
    2020             :     }
    2021          22 :     if (!psOptions->osZField.empty())
    2022             :     {
    2023           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-zfield");
    2024           0 :         return nullptr;
    2025             :     }
    2026          22 :     if (psOptions->oGCPs.nGCPCount)
    2027             :     {
    2028           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-gcp");
    2029           0 :         return nullptr;
    2030             :     }
    2031          22 :     if (!psOptions->aosFieldMap.empty())
    2032             :     {
    2033           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-fieldmap");
    2034           0 :         return nullptr;
    2035             :     }
    2036          22 :     if (psOptions->bForceNullable)
    2037             :     {
    2038           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-forceNullable");
    2039           0 :         return nullptr;
    2040             :     }
    2041          22 :     if (psOptions->bResolveDomains)
    2042             :     {
    2043           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-forceNullable");
    2044           0 :         return nullptr;
    2045             :     }
    2046          22 :     if (psOptions->bEmptyStrAsNull)
    2047             :     {
    2048           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-emptyStrAsNull");
    2049           0 :         return nullptr;
    2050             :     }
    2051          22 :     if (psOptions->bUnsetDefault)
    2052             :     {
    2053           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-unsetDefault");
    2054           0 :         return nullptr;
    2055             :     }
    2056          22 :     if (psOptions->bUnsetFid)
    2057             :     {
    2058           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-unsetFid");
    2059           0 :         return nullptr;
    2060             :     }
    2061          22 :     if (!psOptions->bCopyMD)
    2062             :     {
    2063           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-nomd");
    2064           0 :         return nullptr;
    2065             :     }
    2066          22 :     if (!psOptions->bNativeData)
    2067             :     {
    2068           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-noNativeData");
    2069           0 :         return nullptr;
    2070             :     }
    2071          22 :     if (psOptions->nLimit >= 0)
    2072             :     {
    2073           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-limit");
    2074           0 :         return nullptr;
    2075             :     }
    2076          22 :     if (!psOptions->aosMetadataOptions.empty())
    2077             :     {
    2078           0 :         CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-mo");
    2079           0 :         return nullptr;
    2080             :     }
    2081             : 
    2082          22 :     GDALDataset *poWrkSrcDS = poDS;
    2083          22 :     std::unique_ptr<GDALDataset> poWrkSrcDSToFree;
    2084          22 :     OGR2OGRSpatialReferenceHolder oOutputSRSHolder;
    2085             : 
    2086          22 :     if (!psOptions->osOutputSRSDef.empty())
    2087             :     {
    2088           3 :         oOutputSRSHolder.assignNoRefIncrease(new OGRSpatialReference());
    2089           3 :         oOutputSRSHolder.get()->SetAxisMappingStrategy(
    2090             :             OAMS_TRADITIONAL_GIS_ORDER);
    2091           3 :         if (oOutputSRSHolder.get()->SetFromUserInput(
    2092           3 :                 psOptions->osOutputSRSDef.c_str()) != OGRERR_NONE)
    2093             :         {
    2094           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2095             :                      "Failed to process SRS definition: %s",
    2096             :                      psOptions->osOutputSRSDef.c_str());
    2097           0 :             return nullptr;
    2098             :         }
    2099           3 :         oOutputSRSHolder.get()->SetCoordinateEpoch(
    2100           3 :             psOptions->dfOutputCoordinateEpoch);
    2101             : 
    2102           6 :         poWrkSrcDSToFree = GDALVectorTranslateWrappedDataset::New(
    2103           6 :             poDS, oOutputSRSHolder.get(), psOptions->bTransform);
    2104           3 :         if (poWrkSrcDSToFree == nullptr)
    2105           0 :             return nullptr;
    2106           3 :         poWrkSrcDS = poWrkSrcDSToFree.get();
    2107             :     }
    2108             : 
    2109          22 :     if (!psOptions->osWHERE.empty())
    2110             :     {
    2111             :         // Hack for GMLAS driver
    2112           0 :         if (EQUAL(poDriver->GetDescription(), "GMLAS"))
    2113             :         {
    2114           0 :             if (psOptions->aosLayers.empty())
    2115             :             {
    2116           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2117             :                          "-where not supported by this output driver "
    2118             :                          "without explicit layer name(s)");
    2119           0 :                 return nullptr;
    2120             :             }
    2121             :             else
    2122             :             {
    2123           0 :                 for (const char *pszLayer : psOptions->aosLayers)
    2124             :                 {
    2125           0 :                     OGRLayer *poSrcLayer = poDS->GetLayerByName(pszLayer);
    2126           0 :                     if (poSrcLayer != nullptr)
    2127             :                     {
    2128           0 :                         poSrcLayer->SetAttributeFilter(
    2129           0 :                             psOptions->osWHERE.c_str());
    2130             :                     }
    2131             :                 }
    2132             :             }
    2133             :         }
    2134             :         else
    2135             :         {
    2136           0 :             CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg, "-where");
    2137           0 :             return nullptr;
    2138             :         }
    2139             :     }
    2140             : 
    2141          22 :     if (psOptions->poSpatialFilter)
    2142             :     {
    2143           0 :         for (int i = 0; i < poWrkSrcDS->GetLayerCount(); ++i)
    2144             :         {
    2145           0 :             OGRLayer *poSrcLayer = poWrkSrcDS->GetLayer(i);
    2146           0 :             if (poSrcLayer &&
    2147           0 :                 poSrcLayer->GetLayerDefn()->GetGeomFieldCount() > 0 &&
    2148           0 :                 (psOptions->aosLayers.empty() ||
    2149           0 :                  psOptions->aosLayers.FindString(poSrcLayer->GetName()) >= 0))
    2150             :             {
    2151           0 :                 if (psOptions->bGeomFieldSet)
    2152             :                 {
    2153             :                     const int iGeomField =
    2154           0 :                         poSrcLayer->GetLayerDefn()->GetGeomFieldIndex(
    2155           0 :                             psOptions->osGeomField.c_str());
    2156           0 :                     if (iGeomField >= 0)
    2157           0 :                         poSrcLayer->SetSpatialFilter(
    2158           0 :                             iGeomField, psOptions->poSpatialFilter.get());
    2159             :                     else
    2160           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    2161             :                                  "Cannot find geometry field %s in layer %s. "
    2162             :                                  "Applying to first geometry field",
    2163             :                                  psOptions->osGeomField.c_str(),
    2164           0 :                                  poSrcLayer->GetName());
    2165             :                 }
    2166             :                 else
    2167             :                 {
    2168           0 :                     poSrcLayer->SetSpatialFilter(
    2169           0 :                         psOptions->poSpatialFilter.get());
    2170             :                 }
    2171             :             }
    2172             :         }
    2173             :     }
    2174             : 
    2175          44 :     CPLStringList aosDSCO(psOptions->aosDSCO);
    2176          22 :     if (!psOptions->aosLayers.empty())
    2177             :     {
    2178             :         // Hack for GMLAS driver
    2179           0 :         if (EQUAL(poDriver->GetDescription(), "GMLAS"))
    2180             :         {
    2181           0 :             CPLString osLayers;
    2182           0 :             for (const char *pszLayer : psOptions->aosLayers)
    2183             :             {
    2184           0 :                 if (!osLayers.empty())
    2185           0 :                     osLayers += ",";
    2186           0 :                 osLayers += pszLayer;
    2187             :             }
    2188           0 :             aosDSCO.SetNameValue("LAYERS", osLayers);
    2189             :         }
    2190             :         else
    2191             :         {
    2192           0 :             CPLError(CE_Failure, CPLE_NotSupported, szErrorMsg,
    2193             :                      "Specifying layers");
    2194           0 :             return nullptr;
    2195             :         }
    2196             :     }
    2197             : 
    2198             :     // Hack for GMLAS driver (this speed up deletion by avoiding the GML
    2199             :     // driver to try parsing a pre-existing file). Could be potentially
    2200             :     // removed if the GML driver implemented fast dataset opening (ie
    2201             :     // without parsing) and GetFileList()
    2202          22 :     if (EQUAL(poDriver->GetDescription(), "GMLAS"))
    2203             :     {
    2204          22 :         GDALDriverH hIdentifyingDriver = GDALIdentifyDriver(pszDest, nullptr);
    2205          23 :         if (hIdentifyingDriver != nullptr &&
    2206           1 :             EQUAL(GDALGetDescription(hIdentifyingDriver), "GML"))
    2207             :         {
    2208           0 :             VSIUnlink(pszDest);
    2209           0 :             VSIUnlink(CPLResetExtension(pszDest, "gfs"));
    2210             :         }
    2211             :     }
    2212             : 
    2213             :     GDALDataset *poOut =
    2214          22 :         poDriver->CreateCopy(pszDest, poWrkSrcDS, FALSE, aosDSCO.List(),
    2215          22 :                              psOptions->pfnProgress, psOptions->pProgressData);
    2216             : 
    2217          22 :     return poOut;
    2218             : }
    2219             : 
    2220             : /************************************************************************/
    2221             : /*                           GDALVectorTranslate()                      */
    2222             : /************************************************************************/
    2223             : /**
    2224             :  * Converts vector data between file formats.
    2225             :  *
    2226             :  * This is the equivalent of the <a href="/programs/ogr2ogr.html">ogr2ogr</a>
    2227             :  * utility.
    2228             :  *
    2229             :  * GDALVectorTranslateOptions* must be allocated and freed with
    2230             :  * GDALVectorTranslateOptionsNew() and GDALVectorTranslateOptionsFree()
    2231             :  * respectively. pszDest and hDstDS cannot be used at the same time.
    2232             :  *
    2233             :  * @param pszDest the destination dataset path or NULL.
    2234             :  * @param hDstDS the destination dataset or NULL.
    2235             :  * @param nSrcCount the number of input datasets (only 1 supported currently)
    2236             :  * @param pahSrcDS the list of input datasets.
    2237             :  * @param psOptionsIn the options struct returned by
    2238             :  * GDALVectorTranslateOptionsNew() or NULL.
    2239             :  * @param pbUsageError pointer to a integer output variable to store if any
    2240             :  * usage error has occurred, or NULL.
    2241             :  * @return the output dataset (new dataset that must be closed using
    2242             :  * GDALClose(), or hDstDS is not NULL) or NULL in case of error.
    2243             :  *
    2244             :  * @since GDAL 2.1
    2245             :  */
    2246             : 
    2247         717 : GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS,
    2248             :                                  int nSrcCount, GDALDatasetH *pahSrcDS,
    2249             :                                  const GDALVectorTranslateOptions *psOptionsIn,
    2250             :                                  int *pbUsageError)
    2251             : 
    2252             : {
    2253         717 :     if (pszDest == nullptr && hDstDS == nullptr)
    2254             :     {
    2255           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2256             :                  "pszDest == NULL && hDstDS == NULL");
    2257             : 
    2258           0 :         if (pbUsageError)
    2259           0 :             *pbUsageError = TRUE;
    2260           0 :         return nullptr;
    2261             :     }
    2262         717 :     if (nSrcCount != 1)
    2263             :     {
    2264           0 :         CPLError(CE_Failure, CPLE_AppDefined, "nSrcCount != 1");
    2265             : 
    2266           0 :         if (pbUsageError)
    2267           0 :             *pbUsageError = TRUE;
    2268           0 :         return nullptr;
    2269             :     }
    2270             : 
    2271         717 :     GDALDatasetH hSrcDS = pahSrcDS[0];
    2272         717 :     if (hSrcDS == nullptr)
    2273             :     {
    2274           0 :         CPLError(CE_Failure, CPLE_AppDefined, "hSrcDS == NULL");
    2275             : 
    2276           0 :         if (pbUsageError)
    2277           0 :             *pbUsageError = TRUE;
    2278           0 :         return nullptr;
    2279             :     }
    2280             : 
    2281             :     auto psOptions =
    2282             :         psOptionsIn ? std::make_unique<GDALVectorTranslateOptions>(*psOptionsIn)
    2283        1434 :                     : std::make_unique<GDALVectorTranslateOptions>();
    2284             : 
    2285         717 :     bool bAppend = false;
    2286         717 :     bool bUpdate = false;
    2287         717 :     bool bOverwrite = false;
    2288             : 
    2289         717 :     if (psOptions->eAccessMode == ACCESS_UPDATE)
    2290             :     {
    2291           5 :         bUpdate = true;
    2292             :     }
    2293         712 :     else if (psOptions->eAccessMode == ACCESS_APPEND)
    2294             :     {
    2295          33 :         bAppend = true;
    2296          33 :         bUpdate = true;
    2297             :     }
    2298         679 :     else if (psOptions->eAccessMode == ACCESS_OVERWRITE)
    2299             :     {
    2300          12 :         bOverwrite = true;
    2301          12 :         bUpdate = true;
    2302             :     }
    2303         667 :     else if (hDstDS != nullptr)
    2304             :     {
    2305           5 :         bUpdate = true;
    2306             :     }
    2307             : 
    2308             :     const CPLString osDateLineOffset =
    2309        1434 :         CPLOPrintf("%g", psOptions->dfDateLineOffset);
    2310             : 
    2311         717 :     if (psOptions->bPreserveFID && psOptions->bExplodeCollections)
    2312             :     {
    2313           1 :         CPLError(CE_Failure, CPLE_IllegalArg,
    2314             :                  "cannot use -preserve_fid and -explodecollections at the same "
    2315             :                  "time.");
    2316           1 :         if (pbUsageError)
    2317           1 :             *pbUsageError = TRUE;
    2318           1 :         return nullptr;
    2319             :     }
    2320             : 
    2321         716 :     if (!psOptions->aosFieldMap.empty() && !bAppend)
    2322             :     {
    2323           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    2324             :                  "if -fieldmap is specified, -append must also be specified");
    2325           0 :         if (pbUsageError)
    2326           0 :             *pbUsageError = TRUE;
    2327           0 :         return nullptr;
    2328             :     }
    2329             : 
    2330         716 :     if (!psOptions->aosFieldMap.empty() && psOptions->bAddMissingFields)
    2331             :     {
    2332           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    2333             :                  "if -addfields is specified, -fieldmap cannot be used.");
    2334           0 :         if (pbUsageError)
    2335           0 :             *pbUsageError = TRUE;
    2336           0 :         return nullptr;
    2337             :     }
    2338             : 
    2339         716 :     if (psOptions->bSelFieldsSet && bAppend && !psOptions->bAddMissingFields)
    2340             :     {
    2341           1 :         CPLError(CE_Failure, CPLE_IllegalArg,
    2342             :                  "if -append is specified, -select cannot be used "
    2343             :                  "(use -fieldmap or -sql instead).");
    2344           1 :         if (pbUsageError)
    2345           1 :             *pbUsageError = TRUE;
    2346           1 :         return nullptr;
    2347             :     }
    2348             : 
    2349         715 :     if (!psOptions->aosFieldTypesToString.empty() &&
    2350           0 :         !psOptions->aosMapFieldType.empty())
    2351             :     {
    2352           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    2353             :                  "-fieldTypeToString and -mapFieldType are exclusive.");
    2354           0 :         if (pbUsageError)
    2355           0 :             *pbUsageError = TRUE;
    2356           0 :         return nullptr;
    2357             :     }
    2358             : 
    2359         723 :     if (!psOptions->osSourceSRSDef.empty() &&
    2360         723 :         psOptions->osOutputSRSDef.empty() && psOptions->osSpatSRSDef.empty())
    2361             :     {
    2362           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    2363             :                  "if -s_srs is specified, -t_srs and/or -spat_srs must also be "
    2364             :                  "specified.");
    2365           0 :         if (pbUsageError)
    2366           0 :             *pbUsageError = TRUE;
    2367           0 :         return nullptr;
    2368             :     }
    2369             : 
    2370             :     /* -------------------------------------------------------------------- */
    2371             :     /*      Parse spatial filter SRS if needed.                             */
    2372             :     /* -------------------------------------------------------------------- */
    2373         715 :     std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSpatSRS;
    2374         715 :     if (psOptions->poSpatialFilter && !psOptions->osSpatSRSDef.empty())
    2375             :     {
    2376           4 :         if (!psOptions->osSQLStatement.empty())
    2377             :         {
    2378           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2379             :                      "-spat_srs not compatible with -sql.");
    2380           0 :             return nullptr;
    2381             :         }
    2382           4 :         OGREnvelope sEnvelope;
    2383           4 :         psOptions->poSpatialFilter->getEnvelope(&sEnvelope);
    2384           4 :         poSpatSRS.reset(new OGRSpatialReference());
    2385           4 :         poSpatSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2386           4 :         if (poSpatSRS->SetFromUserInput(psOptions->osSpatSRSDef.c_str()) !=
    2387             :             OGRERR_NONE)
    2388             :         {
    2389           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2390             :                      "Failed to process SRS definition: %s",
    2391           0 :                      psOptions->osSpatSRSDef.c_str());
    2392           0 :             return nullptr;
    2393             :         }
    2394             :     }
    2395             : 
    2396         715 :     if (!psOptions->poClipSrc && !psOptions->osClipSrcDS.empty())
    2397             :     {
    2398           9 :         psOptions->poClipSrc =
    2399          18 :             LoadGeometry(psOptions->osClipSrcDS, psOptions->osClipSrcSQL,
    2400           9 :                          psOptions->osClipSrcLayer, psOptions->osClipSrcWhere,
    2401          18 :                          psOptions->bMakeValid);
    2402           9 :         if (psOptions->poClipSrc == nullptr)
    2403             :         {
    2404           2 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2405             :                      "cannot load source clip geometry");
    2406           2 :             return nullptr;
    2407             :         }
    2408             :     }
    2409         707 :     else if (psOptions->bClipSrc && !psOptions->poClipSrc &&
    2410           1 :              psOptions->poSpatialFilter)
    2411             :     {
    2412           1 :         psOptions->poClipSrc.reset(psOptions->poSpatialFilter->clone());
    2413           1 :         if (poSpatSRS)
    2414             :         {
    2415           0 :             psOptions->poClipSrc->assignSpatialReference(poSpatSRS.get());
    2416             :         }
    2417             :     }
    2418         705 :     else if (psOptions->bClipSrc && !psOptions->poClipSrc)
    2419             :     {
    2420           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    2421             :                  "-clipsrc must be used with -spat option or a\n"
    2422             :                  "bounding box, WKT string or datasource must be specified");
    2423           0 :         if (pbUsageError)
    2424           0 :             *pbUsageError = TRUE;
    2425           0 :         return nullptr;
    2426             :     }
    2427         713 :     if (psOptions->poClipSrc && !psOptions->poClipSrc->IsValid())
    2428             :     {
    2429           2 :         if (!psOptions->bMakeValid)
    2430             :         {
    2431           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2432             :                      "-clipsrc geometry is invalid. You can try to make it "
    2433             :                      "valid with -makevalid, but the results of the operation "
    2434             :                      "should be manually inspected.");
    2435           1 :             return nullptr;
    2436             :         }
    2437             :         auto poValid =
    2438           1 :             std::unique_ptr<OGRGeometry>(psOptions->poClipSrc->MakeValid());
    2439           1 :         if (!poValid)
    2440             :         {
    2441           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2442             :                      "-clipsrc geometry is invalid and cannot be made valid.");
    2443           0 :             return nullptr;
    2444             :         }
    2445           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    2446             :                  "-clipsrc geometry was invalid and has been made valid, "
    2447             :                  "but the results of the operation "
    2448             :                  "should be manually inspected.");
    2449           1 :         psOptions->poClipSrc = std::move(poValid);
    2450             :     }
    2451             : 
    2452         712 :     if (!psOptions->osClipDstDS.empty())
    2453             :     {
    2454           9 :         psOptions->poClipDst =
    2455          18 :             LoadGeometry(psOptions->osClipDstDS, psOptions->osClipDstSQL,
    2456           9 :                          psOptions->osClipDstLayer, psOptions->osClipDstWhere,
    2457          18 :                          psOptions->bMakeValid);
    2458           9 :         if (psOptions->poClipDst == nullptr)
    2459             :         {
    2460           3 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2461             :                      "cannot load dest clip geometry");
    2462           3 :             return nullptr;
    2463             :         }
    2464             :     }
    2465         709 :     if (psOptions->poClipDst && !psOptions->poClipDst->IsValid())
    2466             :     {
    2467           2 :         if (!psOptions->bMakeValid)
    2468             :         {
    2469           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2470             :                      "-clipdst geometry is invalid. You can try to make it "
    2471             :                      "valid with -makevalid, but the results of the operation "
    2472             :                      "should be manually inspected.");
    2473           1 :             return nullptr;
    2474             :         }
    2475             :         auto poValid =
    2476           1 :             std::unique_ptr<OGRGeometry>(psOptions->poClipDst->MakeValid());
    2477           1 :         if (!poValid)
    2478             :         {
    2479           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2480             :                      "-clipdst geometry is invalid and cannot be made valid.");
    2481           0 :             return nullptr;
    2482             :         }
    2483           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    2484             :                  "-clipdst geometry was invalid and has been made valid, "
    2485             :                  "but the results of the operation "
    2486             :                  "should be manually inspected.");
    2487           1 :         psOptions->poClipDst = std::move(poValid);
    2488             :     }
    2489             : 
    2490         708 :     GDALDataset *poDS = GDALDataset::FromHandle(hSrcDS);
    2491         708 :     GDALDataset *poODS = nullptr;
    2492         708 :     GDALDriver *poDriver = nullptr;
    2493        1416 :     CPLString osDestFilename;
    2494             : 
    2495         708 :     if (hDstDS)
    2496             :     {
    2497          13 :         poODS = GDALDataset::FromHandle(hDstDS);
    2498          13 :         osDestFilename = poODS->GetDescription();
    2499             :     }
    2500             :     else
    2501             :     {
    2502         695 :         osDestFilename = pszDest;
    2503             :     }
    2504             : 
    2505             :     /* Various tests to avoid overwriting the source layer(s) */
    2506             :     /* or to avoid appending a layer to itself */
    2507          54 :     if (bUpdate && strcmp(osDestFilename, poDS->GetDescription()) == 0 &&
    2508         762 :         !EQUAL(poDS->GetDriverName(), "Memory") && (bOverwrite || bAppend))
    2509             :     {
    2510           1 :         bool bError = false;
    2511           1 :         if (psOptions->osNewLayerName.empty())
    2512           0 :             bError = true;
    2513           1 :         else if (psOptions->aosLayers.size() == 1)
    2514           1 :             bError = strcmp(psOptions->osNewLayerName.c_str(),
    2515           1 :                             psOptions->aosLayers[0]) == 0;
    2516           0 :         else if (psOptions->osSQLStatement.empty())
    2517           0 :             bError = true;
    2518           1 :         if (bError)
    2519             :         {
    2520           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2521             :                      "-nln name must be specified combined with "
    2522             :                      "a single source layer name,\nor a -sql statement, and "
    2523             :                      "name must be different from an existing layer.");
    2524           0 :             return nullptr;
    2525             :         }
    2526             :     }
    2527         832 :     else if (!bUpdate && strcmp(osDestFilename, poDS->GetDescription()) == 0 &&
    2528          63 :              (psOptions->osFormat.empty() ||
    2529          62 :               !EQUAL(psOptions->osFormat.c_str(), "Memory")))
    2530             :     {
    2531           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2532             :                  "Source and destination datasets must be different "
    2533             :                  "in non-update mode.");
    2534           1 :         return nullptr;
    2535             :     }
    2536             : 
    2537             :     /* -------------------------------------------------------------------- */
    2538             :     /*      Try opening the output datasource as an existing, writable      */
    2539             :     /* -------------------------------------------------------------------- */
    2540        1414 :     std::vector<std::string> aoDrivers;
    2541         707 :     if (poODS == nullptr && psOptions->osFormat.empty())
    2542             :     {
    2543         288 :         aoDrivers = GetOutputDriversFor(pszDest, GDAL_OF_VECTOR);
    2544         288 :         if (!bUpdate && aoDrivers.size() == 1)
    2545             :         {
    2546         245 :             GDALDriverH hDriver = GDALGetDriverByName(aoDrivers[0].c_str());
    2547         245 :             const char *pszPrefix = GDALGetMetadataItem(
    2548             :                 hDriver, GDAL_DMD_CONNECTION_PREFIX, nullptr);
    2549         245 :             if (pszPrefix && STARTS_WITH_CI(pszDest, pszPrefix))
    2550             :             {
    2551           4 :                 bUpdate = true;
    2552             :             }
    2553             :         }
    2554             :     }
    2555             : 
    2556         707 :     if (bUpdate && poODS == nullptr)
    2557             :     {
    2558          45 :         poODS = GDALDataset::Open(
    2559             :             osDestFilename, GDAL_OF_UPDATE | GDAL_OF_VECTOR, nullptr,
    2560          45 :             psOptions->aosDestOpenOptions.List(), nullptr);
    2561             : 
    2562          45 :         if (poODS == nullptr)
    2563             :         {
    2564           1 :             if (bOverwrite || bAppend)
    2565             :             {
    2566           1 :                 poODS = GDALDataset::Open(
    2567             :                     osDestFilename, GDAL_OF_VECTOR, nullptr,
    2568           1 :                     psOptions->aosDestOpenOptions.List(), nullptr);
    2569           1 :                 if (poODS == nullptr)
    2570             :                 {
    2571             :                     /* OK the datasource doesn't exist at all */
    2572           1 :                     bUpdate = false;
    2573             :                 }
    2574             :                 else
    2575             :                 {
    2576           0 :                     poDriver = poODS->GetDriver();
    2577           0 :                     GDALClose(poODS);
    2578           0 :                     poODS = nullptr;
    2579             :                 }
    2580             :             }
    2581             : 
    2582           1 :             if (bUpdate)
    2583             :             {
    2584           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2585             :                          "Unable to open existing output datasource `%s'.",
    2586             :                          osDestFilename.c_str());
    2587           0 :                 return nullptr;
    2588             :             }
    2589             :         }
    2590          44 :         else if (psOptions->aosDSCO.size() > 0)
    2591             :         {
    2592           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2593             :                      "Datasource creation options ignored since an existing "
    2594             :                      "datasource\n"
    2595             :                      "         being updated.");
    2596             :         }
    2597             :     }
    2598             : 
    2599         707 :     if (poODS)
    2600          57 :         poDriver = poODS->GetDriver();
    2601             : 
    2602             :     /* -------------------------------------------------------------------- */
    2603             :     /*      Find the output driver.                                         */
    2604             :     /* -------------------------------------------------------------------- */
    2605         707 :     bool bNewDataSource = false;
    2606         707 :     if (!bUpdate)
    2607             :     {
    2608         650 :         GDALDriverManager *poDM = GetGDALDriverManager();
    2609             : 
    2610         650 :         if (psOptions->osFormat.empty())
    2611             :         {
    2612         254 :             if (aoDrivers.empty())
    2613             :             {
    2614          11 :                 if (EQUAL(CPLGetExtension(pszDest), ""))
    2615             :                 {
    2616          10 :                     psOptions->osFormat = "ESRI Shapefile";
    2617             :                 }
    2618             :                 else
    2619             :                 {
    2620           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2621             :                              "Cannot guess driver for %s", pszDest);
    2622          28 :                     return nullptr;
    2623             :                 }
    2624             :             }
    2625             :             else
    2626             :             {
    2627         243 :                 if (aoDrivers.size() > 1)
    2628             :                 {
    2629           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2630             :                              "Several drivers matching %s extension. Using %s",
    2631           1 :                              CPLGetExtension(pszDest), aoDrivers[0].c_str());
    2632             :                 }
    2633         243 :                 psOptions->osFormat = aoDrivers[0];
    2634             :             }
    2635         253 :             CPLDebug("GDAL", "Using %s driver", psOptions->osFormat.c_str());
    2636             :         }
    2637             : 
    2638         649 :         CPLString osOGRCompatFormat(psOptions->osFormat);
    2639             :         // Special processing for non-unified drivers that have the same name
    2640             :         // as GDAL and OGR drivers. GMT should become OGR_GMT.
    2641             :         // Other candidates could be VRT, SDTS and PDS, but they don't
    2642             :         // have write capabilities. But do the substitution to get a sensible
    2643             :         // error message
    2644         649 :         if (EQUAL(osOGRCompatFormat, "GMT") ||
    2645         648 :             EQUAL(osOGRCompatFormat, "VRT") ||
    2646        1297 :             EQUAL(osOGRCompatFormat, "SDTS") || EQUAL(osOGRCompatFormat, "PDS"))
    2647             :         {
    2648           1 :             osOGRCompatFormat = "OGR_" + osOGRCompatFormat;
    2649             :         }
    2650         649 :         poDriver = poDM->GetDriverByName(osOGRCompatFormat);
    2651         649 :         if (poDriver == nullptr)
    2652             :         {
    2653           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unable to find driver `%s'.",
    2654           0 :                      psOptions->osFormat.c_str());
    2655           0 :             return nullptr;
    2656             :         }
    2657             : 
    2658         649 :         char **papszDriverMD = poDriver->GetMetadata();
    2659         649 :         if (!CPLTestBool(
    2660             :                 CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_VECTOR, "FALSE")))
    2661             :         {
    2662           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2663             :                      "%s driver has no vector capabilities.",
    2664           0 :                      psOptions->osFormat.c_str());
    2665           0 :             return nullptr;
    2666             :         }
    2667             : 
    2668         649 :         if (poDriver->CanVectorTranslateFrom(
    2669         649 :                 pszDest, poDS, psOptions->aosArguments.List(), nullptr))
    2670             :         {
    2671           2 :             return poDriver->VectorTranslateFrom(
    2672           2 :                 pszDest, poDS, psOptions->aosArguments.List(),
    2673           4 :                 psOptions->pfnProgress, psOptions->pProgressData);
    2674             :         }
    2675             : 
    2676         647 :         if (!CPLTestBool(
    2677             :                 CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, "FALSE")))
    2678             :         {
    2679          22 :             if (CPLTestBool(CSLFetchNameValueDef(
    2680             :                     papszDriverMD, GDAL_DCAP_CREATECOPY, "FALSE")))
    2681             :             {
    2682          22 :                 poODS = GDALVectorTranslateCreateCopy(poDriver, pszDest, poDS,
    2683          22 :                                                       psOptions.get());
    2684          22 :                 return poODS;
    2685             :             }
    2686             : 
    2687           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2688             :                      "%s driver does not support data source creation.",
    2689           0 :                      psOptions->osFormat.c_str());
    2690           0 :             return nullptr;
    2691             :         }
    2692             : 
    2693         625 :         if (!psOptions->aosDestOpenOptions.empty())
    2694             :         {
    2695           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2696             :                      "-doo ignored when creating the output datasource.");
    2697             :         }
    2698             : 
    2699             :         /* --------------------------------------------------------------------
    2700             :          */
    2701             :         /*      Special case to improve user experience when translating */
    2702             :         /*      a datasource with multiple layers into a shapefile. If the */
    2703             :         /*      user gives a target datasource with .shp and it does not exist,
    2704             :          */
    2705             :         /*      the shapefile driver will try to create a file, but this is not
    2706             :          */
    2707             :         /*      appropriate because here we have several layers, so create */
    2708             :         /*      a directory instead. */
    2709             :         /* --------------------------------------------------------------------
    2710             :          */
    2711             :         VSIStatBufL sStat;
    2712         696 :         if (EQUAL(poDriver->GetDescription(), "ESRI Shapefile") &&
    2713          71 :             psOptions->osSQLStatement.empty() &&
    2714          68 :             (psOptions->aosLayers.size() > 1 ||
    2715          71 :              (psOptions->aosLayers.empty() && poDS->GetLayerCount() > 1)) &&
    2716           6 :             psOptions->osNewLayerName.empty() &&
    2717         697 :             EQUAL(CPLGetExtension(osDestFilename), "SHP") &&
    2718           1 :             VSIStatL(osDestFilename, &sStat) != 0)
    2719             :         {
    2720           1 :             if (VSIMkdir(osDestFilename, 0755) != 0)
    2721             :             {
    2722           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2723             :                          "Failed to create directory %s\n"
    2724             :                          "for shapefile datastore.",
    2725             :                          osDestFilename.c_str());
    2726           0 :                 return nullptr;
    2727             :             }
    2728             :         }
    2729             : 
    2730         625 :         CPLStringList aosDSCO(psOptions->aosDSCO);
    2731             : 
    2732         625 :         if (!aosDSCO.FetchNameValue("SINGLE_LAYER"))
    2733             :         {
    2734             :             // Informs the target driver (e.g. JSONFG) if a single layer
    2735             :             // will be created
    2736             :             const char *pszCOList =
    2737         625 :                 poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
    2738         626 :             if (pszCOList && strstr(pszCOList, "SINGLE_LAYER") &&
    2739           1 :                 (!psOptions->osSQLStatement.empty() ||
    2740           1 :                  psOptions->aosLayers.size() == 1 ||
    2741           1 :                  (psOptions->aosLayers.empty() && poDS->GetLayerCount() == 1)))
    2742             :             {
    2743           1 :                 aosDSCO.SetNameValue("SINGLE_LAYER", "YES");
    2744             :             }
    2745             :         }
    2746             : 
    2747             :         /* --------------------------------------------------------------------
    2748             :          */
    2749             :         /*      Create the output data source. */
    2750             :         /* --------------------------------------------------------------------
    2751             :          */
    2752         625 :         poODS = poDriver->Create(osDestFilename, 0, 0, 0, GDT_Unknown,
    2753         625 :                                  aosDSCO.List());
    2754         625 :         if (poODS == nullptr)
    2755             :         {
    2756           6 :             CPLError(CE_Failure, CPLE_AppDefined,
    2757             :                      "%s driver failed to create %s",
    2758           3 :                      psOptions->osFormat.c_str(), osDestFilename.c_str());
    2759           3 :             return nullptr;
    2760             :         }
    2761         622 :         bNewDataSource = true;
    2762             : 
    2763         622 :         if (psOptions->bCopyMD)
    2764             :         {
    2765        1236 :             const CPLStringList aosDomains(poDS->GetMetadataDomainList());
    2766         648 :             for (const char *pszMD : aosDomains)
    2767             :             {
    2768          30 :                 if (char **papszMD = poDS->GetMetadata(pszMD))
    2769           7 :                     poODS->SetMetadata(papszMD, pszMD);
    2770             :             }
    2771             :         }
    2772           2 :         for (const auto &[pszKey, pszValue] :
    2773         624 :              cpl::IterateNameValue(psOptions->aosMetadataOptions))
    2774             :         {
    2775           1 :             poODS->SetMetadataItem(pszKey, pszValue);
    2776             :         }
    2777             : 
    2778             :         // When writing to GeoJSON and using -nln, set the @NAME layer
    2779             :         // creation option to avoid the GeoJSON driver to potentially reuse
    2780             :         // the source feature collection name if the input is also GeoJSON.
    2781         633 :         if (!psOptions->osNewLayerName.empty() &&
    2782          11 :             EQUAL(psOptions->osFormat.c_str(), "GeoJSON"))
    2783             :         {
    2784           1 :             psOptions->aosLCO.SetNameValue("@NAME",
    2785           1 :                                            psOptions->osNewLayerName.c_str());
    2786             :         }
    2787             :     }
    2788             : 
    2789             :     // Automatically close poODS on error, if it has been created by this
    2790             :     // method.
    2791        1358 :     GDALDatasetUniquePtr poODSUniquePtr(hDstDS == nullptr ? poODS : nullptr);
    2792             : 
    2793             :     // Some syntaxic sugar to make "ogr2ogr [-f PostgreSQL] PG:dbname=....
    2794             :     // source [srclayer] -lco OVERWRITE=YES" work like "ogr2ogr -overwrite
    2795             :     // PG:dbname=.... source [srclayer]" The former syntax used to work at
    2796             :     // GDAL 1.1.8 time when it was documented in the PG driver, but was broken
    2797             :     // starting with GDAL 1.3.2
    2798             :     // (https://github.com/OSGeo/gdal/commit/29c108a6c9f651dfebae6d1313ba0e707a77c1aa)
    2799             :     // This could probably be generalized to other drivers that support the
    2800             :     // OVERWRITE layer creation option, but we'd need to make sure that they
    2801             :     // just do a DeleteLayer() call. The CARTO driver is an exception regarding
    2802             :     // that.
    2803         697 :     if (EQUAL(poODS->GetDriver()->GetDescription(), "PostgreSQL") &&
    2804          18 :         CPLTestBool(psOptions->aosLCO.FetchNameValueDef("OVERWRITE", "NO")))
    2805             :     {
    2806           0 :         if (bAppend)
    2807             :         {
    2808           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2809             :                      "-append and -lco OVERWRITE=YES are mutually exclusive");
    2810           0 :             return nullptr;
    2811             :         }
    2812           0 :         bOverwrite = true;
    2813             :     }
    2814             : 
    2815             :     /* -------------------------------------------------------------------- */
    2816             :     /*      For random reading                                              */
    2817             :     /* -------------------------------------------------------------------- */
    2818             :     const bool bRandomLayerReading =
    2819         679 :         CPL_TO_BOOL(poDS->TestCapability(ODsCRandomLayerRead));
    2820          17 :     if (bRandomLayerReading && !poODS->TestCapability(ODsCRandomLayerWrite) &&
    2821         696 :         psOptions->aosLayers.size() != 1 && psOptions->osSQLStatement.empty() &&
    2822           0 :         !psOptions->bQuiet)
    2823             :     {
    2824           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2825             :                  "Input datasource uses random layer reading, but "
    2826             :                  "output datasource does not support random layer writing");
    2827             :     }
    2828             : 
    2829         679 :     if (psOptions->nLayerTransaction < 0)
    2830             :     {
    2831         678 :         if (bRandomLayerReading)
    2832          17 :             psOptions->nLayerTransaction = FALSE;
    2833             :         else
    2834         661 :             psOptions->nLayerTransaction =
    2835         661 :                 !poODS->TestCapability(ODsCTransactions);
    2836             :     }
    2837           1 :     else if (psOptions->nLayerTransaction && bRandomLayerReading)
    2838             :     {
    2839           0 :         psOptions->nLayerTransaction = false;
    2840             :     }
    2841             : 
    2842             :     /* -------------------------------------------------------------------- */
    2843             :     /*      Parse the output SRS definition if possible.                    */
    2844             :     /* -------------------------------------------------------------------- */
    2845         679 :     OGR2OGRSpatialReferenceHolder oOutputSRSHolder;
    2846         679 :     if (!psOptions->osOutputSRSDef.empty())
    2847             :     {
    2848         142 :         oOutputSRSHolder.assignNoRefIncrease(new OGRSpatialReference());
    2849         142 :         oOutputSRSHolder.get()->SetAxisMappingStrategy(
    2850             :             OAMS_TRADITIONAL_GIS_ORDER);
    2851         284 :         if (oOutputSRSHolder.get()->SetFromUserInput(
    2852         284 :                 psOptions->osOutputSRSDef.c_str()) != OGRERR_NONE)
    2853             :         {
    2854           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2855             :                      "Failed to process SRS definition: %s",
    2856           0 :                      psOptions->osOutputSRSDef.c_str());
    2857           0 :             return nullptr;
    2858             :         }
    2859         284 :         oOutputSRSHolder.get()->SetCoordinateEpoch(
    2860         142 :             psOptions->dfOutputCoordinateEpoch);
    2861             :     }
    2862             : 
    2863             :     /* -------------------------------------------------------------------- */
    2864             :     /*      Parse the source SRS definition if possible.                    */
    2865             :     /* -------------------------------------------------------------------- */
    2866        1358 :     OGRSpatialReference oSourceSRS;
    2867         679 :     OGRSpatialReference *poSourceSRS = nullptr;
    2868         679 :     if (!psOptions->osSourceSRSDef.empty())
    2869             :     {
    2870           8 :         oSourceSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2871           8 :         if (oSourceSRS.SetFromUserInput(psOptions->osSourceSRSDef.c_str()) !=
    2872             :             OGRERR_NONE)
    2873             :         {
    2874           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2875             :                      "Failed to process SRS definition: %s",
    2876           0 :                      psOptions->osSourceSRSDef.c_str());
    2877           0 :             return nullptr;
    2878             :         }
    2879           8 :         oSourceSRS.SetCoordinateEpoch(psOptions->dfSourceCoordinateEpoch);
    2880           8 :         poSourceSRS = &oSourceSRS;
    2881             :     }
    2882             : 
    2883             :     /* -------------------------------------------------------------------- */
    2884             :     /*      Create a transformation object from the source to               */
    2885             :     /*      destination coordinate system.                                  */
    2886             :     /* -------------------------------------------------------------------- */
    2887         679 :     std::unique_ptr<GCPCoordTransformation> poGCPCoordTrans;
    2888         679 :     if (psOptions->oGCPs.nGCPCount > 0)
    2889             :     {
    2890           7 :         poGCPCoordTrans = std::make_unique<GCPCoordTransformation>(
    2891           7 :             psOptions->oGCPs.nGCPCount, psOptions->oGCPs.pasGCPs,
    2892           7 :             psOptions->nTransformOrder,
    2893          14 :             poSourceSRS ? poSourceSRS : oOutputSRSHolder.get());
    2894           7 :         if (!(poGCPCoordTrans->IsValid()))
    2895             :         {
    2896           1 :             return nullptr;
    2897             :         }
    2898             :     }
    2899             : 
    2900             :     /* -------------------------------------------------------------------- */
    2901             :     /*      Create layer setup and transformer objects.                     */
    2902             :     /* -------------------------------------------------------------------- */
    2903         678 :     SetupTargetLayer oSetup;
    2904         678 :     oSetup.m_poSrcDS = poDS;
    2905         678 :     oSetup.m_poDstDS = poODS;
    2906         678 :     oSetup.m_papszLCO = psOptions->aosLCO.List();
    2907         678 :     oSetup.m_poOutputSRS = oOutputSRSHolder.get();
    2908         678 :     oSetup.m_bTransform = psOptions->bTransform;
    2909         678 :     oSetup.m_bNullifyOutputSRS = psOptions->bNullifyOutputSRS;
    2910         678 :     oSetup.m_poUserSourceSRS = poSourceSRS;
    2911         678 :     oSetup.m_bSelFieldsSet = psOptions->bSelFieldsSet;
    2912         678 :     oSetup.m_papszSelFields = psOptions->aosSelFields.List();
    2913         678 :     oSetup.m_bAppend = bAppend;
    2914         678 :     oSetup.m_bAddMissingFields = psOptions->bAddMissingFields;
    2915         678 :     oSetup.m_eGType = psOptions->eGType;
    2916         678 :     oSetup.m_eGeomTypeConversion = psOptions->eGeomTypeConversion;
    2917         678 :     oSetup.m_nCoordDim = psOptions->nCoordDim;
    2918         678 :     oSetup.m_bOverwrite = bOverwrite;
    2919         678 :     oSetup.m_papszFieldTypesToString = psOptions->aosFieldTypesToString.List();
    2920         678 :     oSetup.m_papszMapFieldType = psOptions->aosMapFieldType.List();
    2921         678 :     oSetup.m_bUnsetFieldWidth = psOptions->bUnsetFieldWidth;
    2922         678 :     oSetup.m_bExplodeCollections = psOptions->bExplodeCollections;
    2923         678 :     oSetup.m_pszZField =
    2924         678 :         psOptions->osZField.empty() ? nullptr : psOptions->osZField.c_str();
    2925         678 :     oSetup.m_papszFieldMap = psOptions->aosFieldMap.List();
    2926         678 :     oSetup.m_pszWHERE =
    2927         678 :         psOptions->osWHERE.empty() ? nullptr : psOptions->osWHERE.c_str();
    2928         678 :     oSetup.m_bExactFieldNameMatch = psOptions->bExactFieldNameMatch;
    2929         678 :     oSetup.m_bQuiet = psOptions->bQuiet;
    2930         678 :     oSetup.m_bForceNullable = psOptions->bForceNullable;
    2931         678 :     oSetup.m_bResolveDomains = psOptions->bResolveDomains;
    2932         678 :     oSetup.m_bUnsetDefault = psOptions->bUnsetDefault;
    2933         678 :     oSetup.m_bUnsetFid = psOptions->bUnsetFid;
    2934         678 :     oSetup.m_bPreserveFID = psOptions->bPreserveFID;
    2935         678 :     oSetup.m_bCopyMD = psOptions->bCopyMD;
    2936         678 :     oSetup.m_bNativeData = psOptions->bNativeData;
    2937         678 :     oSetup.m_bNewDataSource = bNewDataSource;
    2938         678 :     oSetup.m_pszCTPipeline = psOptions->osCTPipeline.empty()
    2939         678 :                                  ? nullptr
    2940           3 :                                  : psOptions->osCTPipeline.c_str();
    2941             : 
    2942        1356 :     LayerTranslator oTranslator;
    2943         678 :     oTranslator.m_poSrcDS = poDS;
    2944         678 :     oTranslator.m_poODS = poODS;
    2945         678 :     oTranslator.m_bTransform = psOptions->bTransform;
    2946         678 :     oTranslator.m_bWrapDateline = psOptions->bWrapDateline;
    2947         678 :     oTranslator.m_osDateLineOffset = osDateLineOffset;
    2948         678 :     oTranslator.m_poOutputSRS = oOutputSRSHolder.get();
    2949         678 :     oTranslator.m_bNullifyOutputSRS = psOptions->bNullifyOutputSRS;
    2950         678 :     oTranslator.m_poUserSourceSRS = poSourceSRS;
    2951         678 :     oTranslator.m_poGCPCoordTrans = poGCPCoordTrans.get();
    2952         678 :     oTranslator.m_eGType = psOptions->eGType;
    2953         678 :     oTranslator.m_eGeomTypeConversion = psOptions->eGeomTypeConversion;
    2954         678 :     oTranslator.m_bMakeValid = psOptions->bMakeValid;
    2955         678 :     oTranslator.m_bSkipInvalidGeom = psOptions->bSkipInvalidGeom;
    2956         678 :     oTranslator.m_nCoordDim = psOptions->nCoordDim;
    2957         678 :     oTranslator.m_eGeomOp = psOptions->eGeomOp;
    2958         678 :     oTranslator.m_dfGeomOpParam = psOptions->dfGeomOpParam;
    2959             :     // Do not emit warning if the user specified directly the clip source geom
    2960         678 :     if (psOptions->osClipSrcDS.empty())
    2961         671 :         oTranslator.m_bWarnedClipSrcSRS = true;
    2962         678 :     oTranslator.m_poClipSrcOri = psOptions->poClipSrc.get();
    2963             :     // Do not emit warning if the user specified directly the clip dest geom
    2964         678 :     if (psOptions->osClipDstDS.empty())
    2965         672 :         oTranslator.m_bWarnedClipDstSRS = true;
    2966         678 :     oTranslator.m_poClipDstOri = psOptions->poClipDst.get();
    2967         678 :     oTranslator.m_bExplodeCollections = psOptions->bExplodeCollections;
    2968         678 :     oTranslator.m_bNativeData = psOptions->bNativeData;
    2969         678 :     oTranslator.m_nLimit = psOptions->nLimit;
    2970             : 
    2971         678 :     if (psOptions->nGroupTransactions)
    2972             :     {
    2973         677 :         if (!psOptions->nLayerTransaction)
    2974         109 :             poODS->StartTransaction(psOptions->bForceTransaction);
    2975             :     }
    2976             : 
    2977         678 :     GIntBig nTotalEventsDone = 0;
    2978             : 
    2979             :     /* -------------------------------------------------------------------- */
    2980             :     /*      Special case for -sql clause.  No source layers required.       */
    2981             :     /* -------------------------------------------------------------------- */
    2982         678 :     int nRetCode = 0;
    2983             : 
    2984         678 :     if (!psOptions->osSQLStatement.empty())
    2985             :     {
    2986             :         /* Special case: if output=input, then we must likely destroy the */
    2987             :         /* old table before to avoid transaction issues. */
    2988          11 :         if (poDS == poODS && !psOptions->osNewLayerName.empty() && bOverwrite)
    2989           0 :             GetLayerAndOverwriteIfNecessary(
    2990           0 :                 poODS, psOptions->osNewLayerName.c_str(), bOverwrite, nullptr,
    2991             :                 nullptr, nullptr);
    2992             : 
    2993          11 :         if (!psOptions->osWHERE.empty())
    2994           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2995             :                      "-where clause ignored in combination with -sql.");
    2996          11 :         if (psOptions->aosLayers.size() > 0)
    2997           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2998             :                      "layer names ignored in combination with -sql.");
    2999             : 
    3000          22 :         OGRLayer *poResultSet = poDS->ExecuteSQL(
    3001          11 :             psOptions->osSQLStatement.c_str(),
    3002          11 :             (!psOptions->bGeomFieldSet) ? psOptions->poSpatialFilter.get()
    3003             :                                         : nullptr,
    3004          11 :             psOptions->osDialect.empty() ? nullptr
    3005          12 :                                          : psOptions->osDialect.c_str());
    3006             : 
    3007          11 :         if (poResultSet != nullptr)
    3008             :         {
    3009          11 :             if (psOptions->poSpatialFilter && psOptions->bGeomFieldSet)
    3010             :             {
    3011           0 :                 int iGeomField = poResultSet->GetLayerDefn()->GetGeomFieldIndex(
    3012           0 :                     psOptions->osGeomField.c_str());
    3013           0 :                 if (iGeomField >= 0)
    3014           0 :                     poResultSet->SetSpatialFilter(
    3015           0 :                         iGeomField, psOptions->poSpatialFilter.get());
    3016             :                 else
    3017           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    3018             :                              "Cannot find geometry field %s.",
    3019           0 :                              psOptions->osGeomField.c_str());
    3020             :             }
    3021             : 
    3022          11 :             GIntBig nCountLayerFeatures = 0;
    3023          11 :             GDALProgressFunc pfnProgress = nullptr;
    3024          11 :             void *pProgressArg = nullptr;
    3025          11 :             if (psOptions->bDisplayProgress)
    3026             :             {
    3027           1 :                 if (bRandomLayerReading)
    3028             :                 {
    3029           1 :                     pfnProgress = psOptions->pfnProgress;
    3030           1 :                     pProgressArg = psOptions->pProgressData;
    3031             :                 }
    3032           0 :                 else if (!poResultSet->TestCapability(OLCFastFeatureCount))
    3033             :                 {
    3034           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    3035             :                              "Progress turned off as fast feature count is not "
    3036             :                              "available.");
    3037           0 :                     psOptions->bDisplayProgress = false;
    3038             :                 }
    3039             :                 else
    3040             :                 {
    3041           0 :                     nCountLayerFeatures = poResultSet->GetFeatureCount();
    3042           0 :                     pfnProgress = psOptions->pfnProgress;
    3043           0 :                     pProgressArg = psOptions->pProgressData;
    3044             :                 }
    3045             :             }
    3046             : 
    3047          11 :             OGRLayer *poPassedLayer = poResultSet;
    3048          11 :             if (psOptions->bSplitListFields)
    3049             :             {
    3050             :                 auto poLayer = new OGRSplitListFieldLayer(
    3051           0 :                     poPassedLayer, psOptions->nMaxSplitListSubFields);
    3052           0 :                 poPassedLayer = poLayer;
    3053           0 :                 int nRet = poLayer->BuildLayerDefn(nullptr, nullptr);
    3054           0 :                 if (!nRet)
    3055             :                 {
    3056           0 :                     delete poPassedLayer;
    3057           0 :                     poPassedLayer = poResultSet;
    3058             :                 }
    3059             :             }
    3060             : 
    3061             :             /* --------------------------------------------------------------------
    3062             :              */
    3063             :             /*      Special case to improve user experience when translating
    3064             :              * into   */
    3065             :             /*      single file shapefile and source has only one layer, and
    3066             :              * that   */
    3067             :             /*      the layer name isn't specified */
    3068             :             /* --------------------------------------------------------------------
    3069             :              */
    3070             :             VSIStatBufL sStat;
    3071          14 :             if (EQUAL(poDriver->GetDescription(), "ESRI Shapefile") &&
    3072           3 :                 psOptions->osNewLayerName.empty() &&
    3073           3 :                 VSIStatL(osDestFilename, &sStat) == 0 &&
    3074          14 :                 VSI_ISREG(sStat.st_mode) &&
    3075           0 :                 (EQUAL(CPLGetExtension(osDestFilename), "shp") ||
    3076           0 :                  EQUAL(CPLGetExtension(osDestFilename), "shz") ||
    3077           0 :                  EQUAL(CPLGetExtension(osDestFilename), "dbf")))
    3078             :             {
    3079           0 :                 psOptions->osNewLayerName = CPLGetBasename(osDestFilename);
    3080             :             }
    3081             : 
    3082             :             auto psInfo = oSetup.Setup(poPassedLayer,
    3083          11 :                                        psOptions->osNewLayerName.empty()
    3084             :                                            ? nullptr
    3085           1 :                                            : psOptions->osNewLayerName.c_str(),
    3086          34 :                                        psOptions.get(), nTotalEventsDone);
    3087             : 
    3088          11 :             poPassedLayer->ResetReading();
    3089             : 
    3090          22 :             if (psInfo == nullptr ||
    3091          11 :                 !oTranslator.Translate(nullptr, psInfo.get(),
    3092             :                                        nCountLayerFeatures, nullptr,
    3093             :                                        nTotalEventsDone, pfnProgress,
    3094          11 :                                        pProgressArg, psOptions.get()))
    3095             :             {
    3096           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3097             :                          "Terminating translation prematurely after failed\n"
    3098             :                          "translation from sql statement.");
    3099             : 
    3100           0 :                 nRetCode = 1;
    3101             :             }
    3102             : 
    3103          11 :             if (poPassedLayer != poResultSet)
    3104           0 :                 delete poPassedLayer;
    3105             : 
    3106          11 :             poDS->ReleaseResultSet(poResultSet);
    3107             :         }
    3108             :         else
    3109             :         {
    3110           0 :             if (CPLGetLastErrorNo() != 0)
    3111           0 :                 nRetCode = 1;
    3112             :         }
    3113             :     }
    3114             : 
    3115             :     /* -------------------------------------------------------------------- */
    3116             :     /*      Special case for layer interleaving mode.                       */
    3117             :     /* -------------------------------------------------------------------- */
    3118         667 :     else if (bRandomLayerReading)
    3119             :     {
    3120          16 :         if (psOptions->bSplitListFields)
    3121             :         {
    3122           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3123             :                      "-splitlistfields not supported in this mode");
    3124           0 :             return nullptr;
    3125             :         }
    3126             : 
    3127             :         // Make sure to probe all layers in case some are by default invisible
    3128          28 :         for (const char *pszLayer : psOptions->aosLayers)
    3129             :         {
    3130          12 :             OGRLayer *poLayer = poDS->GetLayerByName(pszLayer);
    3131             : 
    3132          12 :             if (poLayer == nullptr)
    3133             :             {
    3134           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3135             :                          "Couldn't fetch requested layer %s!", pszLayer);
    3136           0 :                 return nullptr;
    3137             :             }
    3138             :         }
    3139             : 
    3140          16 :         const int nSrcLayerCount = poDS->GetLayerCount();
    3141          16 :         std::vector<AssociatedLayers> pasAssocLayers(nSrcLayerCount);
    3142             : 
    3143             :         /* --------------------------------------------------------------------
    3144             :          */
    3145             :         /*      Special case to improve user experience when translating into */
    3146             :         /*      single file shapefile and source has only one layer, and that */
    3147             :         /*      the layer name isn't specified */
    3148             :         /* --------------------------------------------------------------------
    3149             :          */
    3150             :         VSIStatBufL sStat;
    3151          16 :         if (EQUAL(poDriver->GetDescription(), "ESRI Shapefile") &&
    3152           5 :             (psOptions->aosLayers.size() == 1 || nSrcLayerCount == 1) &&
    3153           0 :             psOptions->osNewLayerName.empty() &&
    3154          21 :             VSIStatL(osDestFilename, &sStat) == 0 && VSI_ISREG(sStat.st_mode) &&
    3155           0 :             (EQUAL(CPLGetExtension(osDestFilename), "shp") ||
    3156           0 :              EQUAL(CPLGetExtension(osDestFilename), "shz") ||
    3157           0 :              EQUAL(CPLGetExtension(osDestFilename), "dbf")))
    3158             :         {
    3159           0 :             psOptions->osNewLayerName = CPLGetBasename(osDestFilename);
    3160             :         }
    3161             : 
    3162          16 :         GDALProgressFunc pfnProgress = nullptr;
    3163          16 :         void *pProgressArg = nullptr;
    3164          16 :         if (!psOptions->bQuiet)
    3165             :         {
    3166          16 :             pfnProgress = psOptions->pfnProgress;
    3167          16 :             pProgressArg = psOptions->pProgressData;
    3168             :         }
    3169             : 
    3170             :         /* --------------------------------------------------------------------
    3171             :          */
    3172             :         /*      If no target layer specified, use all source layers. */
    3173             :         /* --------------------------------------------------------------------
    3174             :          */
    3175          16 :         if (psOptions->aosLayers.empty())
    3176             :         {
    3177         148 :             for (int iLayer = 0; iLayer < nSrcLayerCount; iLayer++)
    3178             :             {
    3179         135 :                 OGRLayer *poLayer = poDS->GetLayer(iLayer);
    3180             : 
    3181         135 :                 if (poLayer == nullptr)
    3182             :                 {
    3183           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    3184             :                              "Couldn't fetch advertised layer %d!", iLayer);
    3185           0 :                     return nullptr;
    3186             :                 }
    3187             : 
    3188         135 :                 psOptions->aosLayers.AddString(poLayer->GetName());
    3189             :             }
    3190             :         }
    3191             :         else
    3192             :         {
    3193           3 :             const bool bSrcIsOSM = (strcmp(poDS->GetDriverName(), "OSM") == 0);
    3194           3 :             if (bSrcIsOSM)
    3195             :             {
    3196           6 :                 CPLString osInterestLayers = "SET interest_layers =";
    3197          15 :                 for (int iLayer = 0; iLayer < psOptions->aosLayers.size();
    3198             :                      iLayer++)
    3199             :                 {
    3200          12 :                     if (iLayer != 0)
    3201           9 :                         osInterestLayers += ",";
    3202          12 :                     osInterestLayers += psOptions->aosLayers[iLayer];
    3203             :                 }
    3204             : 
    3205           3 :                 poDS->ExecuteSQL(osInterestLayers.c_str(), nullptr, nullptr);
    3206             :             }
    3207             :         }
    3208             : 
    3209             :         /* --------------------------------------------------------------------
    3210             :          */
    3211             :         /*      First pass to set filters. */
    3212             :         /* --------------------------------------------------------------------
    3213             :          */
    3214          16 :         std::map<OGRLayer *, int> oMapLayerToIdx;
    3215             : 
    3216         166 :         for (int iLayer = 0; iLayer < nSrcLayerCount; iLayer++)
    3217             :         {
    3218         150 :             OGRLayer *poLayer = poDS->GetLayer(iLayer);
    3219         150 :             if (poLayer == nullptr)
    3220             :             {
    3221           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3222             :                          "Couldn't fetch advertised layer %d!", iLayer);
    3223           0 :                 return nullptr;
    3224             :             }
    3225             : 
    3226         150 :             pasAssocLayers[iLayer].poSrcLayer = poLayer;
    3227             : 
    3228         150 :             if (psOptions->aosLayers.FindString(poLayer->GetName()) >= 0)
    3229             :             {
    3230         147 :                 if (!psOptions->osWHERE.empty())
    3231             :                 {
    3232           0 :                     if (poLayer->SetAttributeFilter(
    3233           0 :                             psOptions->osWHERE.c_str()) != OGRERR_NONE)
    3234             :                     {
    3235           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    3236             :                                  "SetAttributeFilter(%s) on layer '%s' failed.",
    3237           0 :                                  psOptions->osWHERE.c_str(),
    3238           0 :                                  poLayer->GetName());
    3239           0 :                         if (!psOptions->bSkipFailures)
    3240             :                         {
    3241           0 :                             return nullptr;
    3242             :                         }
    3243             :                     }
    3244             :                 }
    3245             : 
    3246         294 :                 ApplySpatialFilter(
    3247         147 :                     poLayer, psOptions->poSpatialFilter.get(), poSpatSRS.get(),
    3248         147 :                     psOptions->bGeomFieldSet ? psOptions->osGeomField.c_str()
    3249             :                                              : nullptr,
    3250             :                     poSourceSRS);
    3251             : 
    3252         147 :                 oMapLayerToIdx[poLayer] = iLayer;
    3253             :             }
    3254             :         }
    3255             : 
    3256             :         /* --------------------------------------------------------------------
    3257             :          */
    3258             :         /*      Second pass to process features in a interleaved layer mode. */
    3259             :         /* --------------------------------------------------------------------
    3260             :          */
    3261          16 :         bool bTargetLayersHaveBeenCreated = false;
    3262             :         while (true)
    3263             :         {
    3264         991 :             OGRLayer *poFeatureLayer = nullptr;
    3265             :             auto poFeature = std::unique_ptr<OGRFeature>(poDS->GetNextFeature(
    3266         991 :                 &poFeatureLayer, nullptr, pfnProgress, pProgressArg));
    3267         991 :             if (poFeature == nullptr)
    3268          16 :                 break;
    3269             :             std::map<OGRLayer *, int>::const_iterator oIter =
    3270         975 :                 oMapLayerToIdx.find(poFeatureLayer);
    3271         975 :             if (oIter == oMapLayerToIdx.end())
    3272             :             {
    3273             :                 // Feature in a layer that is not a layer of interest.
    3274             :                 // nothing to do
    3275             :             }
    3276             :             else
    3277             :             {
    3278         975 :                 if (!bTargetLayersHaveBeenCreated)
    3279             :                 {
    3280             :                     // We defer target layer creation at the first feature
    3281             :                     // retrieved since getting the layer definition can be
    3282             :                     // costly (case of the GMLAS driver) and thus we'd better
    3283             :                     // taking advantage from the progress callback of
    3284             :                     // GetNextFeature.
    3285          15 :                     bTargetLayersHaveBeenCreated = true;
    3286         160 :                     for (int iLayer = 0; iLayer < nSrcLayerCount; iLayer++)
    3287             :                     {
    3288         145 :                         OGRLayer *poLayer = poDS->GetLayer(iLayer);
    3289         290 :                         if (psOptions->aosLayers.FindString(
    3290         290 :                                 poLayer->GetName()) < 0)
    3291           3 :                             continue;
    3292             : 
    3293             :                         auto psInfo = oSetup.Setup(
    3294             :                             poLayer,
    3295         142 :                             psOptions->osNewLayerName.empty()
    3296             :                                 ? nullptr
    3297           0 :                                 : psOptions->osNewLayerName.c_str(),
    3298         284 :                             psOptions.get(), nTotalEventsDone);
    3299             : 
    3300         142 :                         if (psInfo == nullptr && !psOptions->bSkipFailures)
    3301             :                         {
    3302           0 :                             return nullptr;
    3303             :                         }
    3304             : 
    3305         142 :                         pasAssocLayers[iLayer].psInfo = std::move(psInfo);
    3306             :                     }
    3307          15 :                     if (nRetCode)
    3308           0 :                         break;
    3309             :                 }
    3310             : 
    3311         975 :                 int iLayer = oIter->second;
    3312         975 :                 TargetLayerInfo *psInfo = pasAssocLayers[iLayer].psInfo.get();
    3313        1949 :                 if ((psInfo == nullptr ||
    3314         974 :                      !oTranslator.Translate(poFeature.release(), psInfo, 0,
    3315             :                                             nullptr, nTotalEventsDone, nullptr,
    3316        1950 :                                             nullptr, psOptions.get())) &&
    3317           1 :                     !psOptions->bSkipFailures)
    3318             :                 {
    3319           0 :                     CPLError(
    3320             :                         CE_Failure, CPLE_AppDefined,
    3321             :                         "Terminating translation prematurely after failed\n"
    3322             :                         "translation of layer %s (use -skipfailures to skip "
    3323             :                         "errors)",
    3324           0 :                         poFeatureLayer->GetName());
    3325             : 
    3326           0 :                     nRetCode = 1;
    3327           0 :                     break;
    3328             :                 }
    3329             :             }
    3330         975 :         }  // while true
    3331             : 
    3332          16 :         if (pfnProgress)
    3333             :         {
    3334           0 :             pfnProgress(1.0, "", pProgressArg);
    3335             :         }
    3336             : 
    3337          16 :         if (!bTargetLayersHaveBeenCreated)
    3338             :         {
    3339             :             // bTargetLayersHaveBeenCreated not used after here.
    3340             :             // bTargetLayersHaveBeenCreated = true;
    3341           6 :             for (int iLayer = 0; iLayer < nSrcLayerCount; iLayer++)
    3342             :             {
    3343           5 :                 OGRLayer *poLayer = poDS->GetLayer(iLayer);
    3344           5 :                 if (psOptions->aosLayers.FindString(poLayer->GetName()) < 0)
    3345           0 :                     continue;
    3346             : 
    3347             :                 auto psInfo =
    3348             :                     oSetup.Setup(poLayer,
    3349           5 :                                  psOptions->osNewLayerName.empty()
    3350             :                                      ? nullptr
    3351           0 :                                      : psOptions->osNewLayerName.c_str(),
    3352          10 :                                  psOptions.get(), nTotalEventsDone);
    3353             : 
    3354           5 :                 if (psInfo == nullptr && !psOptions->bSkipFailures)
    3355             :                 {
    3356           0 :                     return nullptr;
    3357             :                 }
    3358             : 
    3359           5 :                 pasAssocLayers[iLayer].psInfo = std::move(psInfo);
    3360             :             }
    3361             :         }
    3362             :     }
    3363             : 
    3364             :     else
    3365             :     {
    3366         651 :         std::vector<OGRLayer *> apoLayers;
    3367             : 
    3368             :         /* --------------------------------------------------------------------
    3369             :          */
    3370             :         /*      Process each data source layer. */
    3371             :         /* --------------------------------------------------------------------
    3372             :          */
    3373         651 :         if (psOptions->aosLayers.empty())
    3374             :         {
    3375         642 :             const int nLayerCount = poDS->GetLayerCount();
    3376             : 
    3377        1316 :             for (int iLayer = 0; iLayer < nLayerCount; iLayer++)
    3378             :             {
    3379         674 :                 OGRLayer *poLayer = poDS->GetLayer(iLayer);
    3380             : 
    3381         674 :                 if (poLayer == nullptr)
    3382             :                 {
    3383           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    3384             :                              "Couldn't fetch advertised layer %d!", iLayer);
    3385           0 :                     return nullptr;
    3386             :                 }
    3387         674 :                 if (!poDS->IsLayerPrivate(iLayer))
    3388             :                 {
    3389         674 :                     apoLayers.push_back(poLayer);
    3390             :                 }
    3391             :             }
    3392             :         }
    3393             :         /* --------------------------------------------------------------------
    3394             :          */
    3395             :         /*      Process specified data source layers. */
    3396             :         /* --------------------------------------------------------------------
    3397             :          */
    3398             :         else
    3399             :         {
    3400             : 
    3401          24 :             for (int iLayer = 0; psOptions->aosLayers[iLayer] != nullptr;
    3402             :                  iLayer++)
    3403             :             {
    3404             :                 OGRLayer *poLayer =
    3405          15 :                     poDS->GetLayerByName(psOptions->aosLayers[iLayer]);
    3406             : 
    3407          15 :                 if (poLayer == nullptr)
    3408             :                 {
    3409           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    3410             :                              "Couldn't fetch requested layer '%s'!",
    3411           0 :                              psOptions->aosLayers[iLayer]);
    3412           0 :                     if (!psOptions->bSkipFailures)
    3413             :                     {
    3414           0 :                         return nullptr;
    3415             :                     }
    3416             :                 }
    3417             : 
    3418          15 :                 apoLayers.emplace_back(poLayer);
    3419             :             }
    3420             :         }
    3421             : 
    3422             :         /* --------------------------------------------------------------------
    3423             :          */
    3424             :         /*      Special case to improve user experience when translating into */
    3425             :         /*      single file shapefile and source has only one layer, and that */
    3426             :         /*      the layer name isn't specified */
    3427             :         /* --------------------------------------------------------------------
    3428             :          */
    3429             :         VSIStatBufL sStat;
    3430         651 :         const int nLayerCount = static_cast<int>(apoLayers.size());
    3431         734 :         if (EQUAL(poDriver->GetDescription(), "ESRI Shapefile") &&
    3432          82 :             nLayerCount == 1 && psOptions->osNewLayerName.empty() &&
    3433         744 :             VSIStatL(osDestFilename, &sStat) == 0 && VSI_ISREG(sStat.st_mode) &&
    3434          10 :             (EQUAL(CPLGetExtension(osDestFilename), "shp") ||
    3435           2 :              EQUAL(CPLGetExtension(osDestFilename), "shz") ||
    3436           1 :              EQUAL(CPLGetExtension(osDestFilename), "dbf")))
    3437             :         {
    3438           9 :             psOptions->osNewLayerName = CPLGetBasename(osDestFilename);
    3439             :         }
    3440             : 
    3441         651 :         std::vector<GIntBig> anLayerCountFeatures;
    3442         651 :         anLayerCountFeatures.resize(nLayerCount);
    3443         651 :         GIntBig nCountLayersFeatures = 0;
    3444         651 :         GIntBig nAccCountFeatures = 0;
    3445             : 
    3446             :         /* First pass to apply filters and count all features if necessary */
    3447        1340 :         for (int iLayer = 0; iLayer < nLayerCount; iLayer++)
    3448             :         {
    3449         689 :             OGRLayer *poLayer = apoLayers[iLayer];
    3450         689 :             if (poLayer == nullptr)
    3451           0 :                 continue;
    3452             : 
    3453         689 :             if (!psOptions->osWHERE.empty())
    3454             :             {
    3455           8 :                 if (poLayer->SetAttributeFilter(psOptions->osWHERE.c_str()) !=
    3456             :                     OGRERR_NONE)
    3457             :                 {
    3458           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    3459             :                              "SetAttributeFilter(%s) on layer '%s' failed.",
    3460           0 :                              psOptions->osWHERE.c_str(), poLayer->GetName());
    3461           0 :                     if (!psOptions->bSkipFailures)
    3462             :                     {
    3463           0 :                         return nullptr;
    3464             :                     }
    3465             :                 }
    3466             :             }
    3467             : 
    3468        1377 :             ApplySpatialFilter(
    3469         689 :                 poLayer, psOptions->poSpatialFilter.get(), poSpatSRS.get(),
    3470         689 :                 psOptions->bGeomFieldSet ? psOptions->osGeomField.c_str()
    3471             :                                          : nullptr,
    3472             :                 poSourceSRS);
    3473             : 
    3474         689 :             if (psOptions->bDisplayProgress)
    3475             :             {
    3476           3 :                 if (!poLayer->TestCapability(OLCFastFeatureCount))
    3477             :                 {
    3478           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    3479             :                              "Progress turned off as fast feature count is not "
    3480             :                              "available.");
    3481           0 :                     psOptions->bDisplayProgress = false;
    3482             :                 }
    3483             :                 else
    3484             :                 {
    3485           3 :                     anLayerCountFeatures[iLayer] = poLayer->GetFeatureCount();
    3486           3 :                     if (psOptions->nLimit >= 0)
    3487           0 :                         anLayerCountFeatures[iLayer] = std::min(
    3488           0 :                             anLayerCountFeatures[iLayer], psOptions->nLimit);
    3489           3 :                     nCountLayersFeatures += anLayerCountFeatures[iLayer];
    3490             :                 }
    3491             :             }
    3492             :         }
    3493             : 
    3494             :         /* Second pass to do the real job */
    3495        1340 :         for (int iLayer = 0; iLayer < nLayerCount && nRetCode == 0; iLayer++)
    3496             :         {
    3497         689 :             OGRLayer *poLayer = apoLayers[iLayer];
    3498         689 :             if (poLayer == nullptr)
    3499           0 :                 continue;
    3500             : 
    3501         689 :             GDALProgressFunc pfnProgress = nullptr;
    3502         689 :             void *pProgressArg = nullptr;
    3503             : 
    3504         689 :             OGRLayer *poPassedLayer = poLayer;
    3505         689 :             if (psOptions->bSplitListFields)
    3506             :             {
    3507             :                 auto poSLFLayer = new OGRSplitListFieldLayer(
    3508           1 :                     poPassedLayer, psOptions->nMaxSplitListSubFields);
    3509           1 :                 poPassedLayer = poSLFLayer;
    3510             : 
    3511           1 :                 if (psOptions->bDisplayProgress &&
    3512           1 :                     psOptions->nMaxSplitListSubFields != 1 &&
    3513             :                     nCountLayersFeatures != 0)
    3514             :                 {
    3515           0 :                     pfnProgress = GDALScaledProgress;
    3516           0 :                     pProgressArg = GDALCreateScaledProgress(
    3517             :                         nAccCountFeatures * 1.0 / nCountLayersFeatures,
    3518           0 :                         (nAccCountFeatures + anLayerCountFeatures[iLayer] / 2) *
    3519             :                             1.0 / nCountLayersFeatures,
    3520           0 :                         psOptions->pfnProgress, psOptions->pProgressData);
    3521             :                 }
    3522             :                 else
    3523             :                 {
    3524           1 :                     pfnProgress = nullptr;
    3525           1 :                     pProgressArg = nullptr;
    3526             :                 }
    3527             : 
    3528             :                 int nRet =
    3529           1 :                     poSLFLayer->BuildLayerDefn(pfnProgress, pProgressArg);
    3530           1 :                 if (!nRet)
    3531             :                 {
    3532           0 :                     delete poPassedLayer;
    3533           0 :                     poPassedLayer = poLayer;
    3534             :                 }
    3535             : 
    3536           1 :                 if (psOptions->bDisplayProgress)
    3537           0 :                     GDALDestroyScaledProgress(pProgressArg);
    3538           1 :                 pfnProgress = nullptr;
    3539           1 :                 pProgressArg = nullptr;
    3540             :             }
    3541             : 
    3542         689 :             if (psOptions->bDisplayProgress)
    3543             :             {
    3544           3 :                 if (nCountLayersFeatures != 0)
    3545             :                 {
    3546           3 :                     pfnProgress = GDALScaledProgress;
    3547           3 :                     GIntBig nStart = 0;
    3548           3 :                     if (poPassedLayer != poLayer &&
    3549           0 :                         psOptions->nMaxSplitListSubFields != 1)
    3550           0 :                         nStart = anLayerCountFeatures[iLayer] / 2;
    3551           3 :                     pProgressArg = GDALCreateScaledProgress(
    3552           3 :                         (nAccCountFeatures + nStart) * 1.0 /
    3553             :                             nCountLayersFeatures,
    3554           3 :                         (nAccCountFeatures + anLayerCountFeatures[iLayer]) *
    3555             :                             1.0 / nCountLayersFeatures,
    3556           3 :                         psOptions->pfnProgress, psOptions->pProgressData);
    3557             :                 }
    3558             :             }
    3559             : 
    3560         689 :             nAccCountFeatures += anLayerCountFeatures[iLayer];
    3561             : 
    3562             :             auto psInfo = oSetup.Setup(poPassedLayer,
    3563         689 :                                        psOptions->osNewLayerName.empty()
    3564             :                                            ? nullptr
    3565          30 :                                            : psOptions->osNewLayerName.c_str(),
    3566        2097 :                                        psOptions.get(), nTotalEventsDone);
    3567             : 
    3568         689 :             poPassedLayer->ResetReading();
    3569             : 
    3570         689 :             if ((psInfo == nullptr ||
    3571         687 :                  !oTranslator.Translate(nullptr, psInfo.get(),
    3572         687 :                                         anLayerCountFeatures[iLayer], nullptr,
    3573             :                                         nTotalEventsDone, pfnProgress,
    3574        1378 :                                         pProgressArg, psOptions.get())) &&
    3575           8 :                 !psOptions->bSkipFailures)
    3576             :             {
    3577           7 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3578             :                          "Terminating translation prematurely after failed\n"
    3579             :                          "translation of layer %s (use -skipfailures to skip "
    3580             :                          "errors)",
    3581           7 :                          poLayer->GetName());
    3582             : 
    3583           7 :                 nRetCode = 1;
    3584             :             }
    3585             : 
    3586         689 :             if (poPassedLayer != poLayer)
    3587           1 :                 delete poPassedLayer;
    3588             : 
    3589         689 :             if (psOptions->bDisplayProgress)
    3590           3 :                 GDALDestroyScaledProgress(pProgressArg);
    3591             :         }
    3592             :     }
    3593             :     /* -------------------------------------------------------------------- */
    3594             :     /*      Process DS style table                                          */
    3595             :     /* -------------------------------------------------------------------- */
    3596             : 
    3597         678 :     poODS->SetStyleTable(poDS->GetStyleTable());
    3598             : 
    3599         678 :     if (psOptions->nGroupTransactions)
    3600             :     {
    3601         677 :         if (!psOptions->nLayerTransaction)
    3602             :         {
    3603         109 :             if (nRetCode != 0 && !psOptions->bSkipFailures)
    3604           6 :                 poODS->RollbackTransaction();
    3605             :             else
    3606             :             {
    3607         103 :                 OGRErr eRet = poODS->CommitTransaction();
    3608         103 :                 if (eRet != OGRERR_NONE && eRet != OGRERR_UNSUPPORTED_OPERATION)
    3609             :                 {
    3610           1 :                     nRetCode = 1;
    3611             :                 }
    3612             :             }
    3613             :         }
    3614             :     }
    3615             : 
    3616             :     // Note: this guarantees that the file can be opened in a consistent state,
    3617             :     // without requiring to close poODS, only if the driver declares
    3618             :     // DCAP_FLUSHCACHE_CONSISTENT_STATE
    3619         678 :     if (poODS->FlushCache() != CE_None)
    3620           0 :         nRetCode = 1;
    3621             : 
    3622         678 :     if (nRetCode == 0)
    3623             :     {
    3624         670 :         if (hDstDS)
    3625          13 :             return hDstDS;
    3626             :         else
    3627         657 :             return GDALDataset::ToHandle(poODSUniquePtr.release());
    3628             :     }
    3629             : 
    3630           8 :     return nullptr;
    3631             : }
    3632             : 
    3633             : /************************************************************************/
    3634             : /*                               SetZ()                                 */
    3635             : /************************************************************************/
    3636             : 
    3637             : namespace
    3638             : {
    3639             : class SetZVisitor : public OGRDefaultGeometryVisitor
    3640             : {
    3641             :     double m_dfZ;
    3642             : 
    3643             :   public:
    3644          30 :     explicit SetZVisitor(double dfZ) : m_dfZ(dfZ)
    3645             :     {
    3646          30 :     }
    3647             : 
    3648             :     using OGRDefaultGeometryVisitor::visit;
    3649             : 
    3650         735 :     void visit(OGRPoint *poPoint) override
    3651             :     {
    3652         735 :         poPoint->setZ(m_dfZ);
    3653         735 :     }
    3654             : };
    3655             : }  // namespace
    3656             : 
    3657          30 : static void SetZ(OGRGeometry *poGeom, double dfZ)
    3658             : {
    3659          30 :     if (poGeom == nullptr)
    3660           0 :         return;
    3661          60 :     SetZVisitor visitor(dfZ);
    3662          30 :     poGeom->set3D(true);
    3663          30 :     poGeom->accept(&visitor);
    3664             : }
    3665             : 
    3666             : /************************************************************************/
    3667             : /*                       ForceCoordDimension()                          */
    3668             : /************************************************************************/
    3669             : 
    3670         967 : static int ForceCoordDimension(int eGType, int nCoordDim)
    3671             : {
    3672         967 :     if (nCoordDim == 2 && eGType != wkbNone)
    3673           3 :         return wkbFlatten(eGType);
    3674         964 :     else if (nCoordDim == 3 && eGType != wkbNone)
    3675           3 :         return wkbSetZ(wkbFlatten(eGType));
    3676         961 :     else if (nCoordDim == COORD_DIM_XYM && eGType != wkbNone)
    3677           2 :         return wkbSetM(wkbFlatten(eGType));
    3678         959 :     else if (nCoordDim == 4 && eGType != wkbNone)
    3679           2 :         return OGR_GT_SetModifier(static_cast<OGRwkbGeometryType>(eGType), TRUE,
    3680           2 :                                   TRUE);
    3681             :     else
    3682         957 :         return eGType;
    3683             : }
    3684             : 
    3685             : /************************************************************************/
    3686             : /*                   GetLayerAndOverwriteIfNecessary()                  */
    3687             : /************************************************************************/
    3688             : 
    3689         847 : static OGRLayer *GetLayerAndOverwriteIfNecessary(GDALDataset *poDstDS,
    3690             :                                                  const char *pszNewLayerName,
    3691             :                                                  bool bOverwrite,
    3692             :                                                  bool *pbErrorOccurred,
    3693             :                                                  bool *pbOverwriteActuallyDone,
    3694             :                                                  bool *pbAddOverwriteLCO)
    3695             : {
    3696         847 :     if (pbErrorOccurred)
    3697         847 :         *pbErrorOccurred = false;
    3698         847 :     if (pbOverwriteActuallyDone)
    3699         847 :         *pbOverwriteActuallyDone = false;
    3700         847 :     if (pbAddOverwriteLCO)
    3701         847 :         *pbAddOverwriteLCO = false;
    3702             : 
    3703             :     /* GetLayerByName() can instantiate layers that would have been */
    3704             :     /* 'hidden' otherwise, for example, non-spatial tables in a */
    3705             :     /* PostGIS-enabled database, so this apparently useless command is */
    3706             :     /* not useless. (#4012) */
    3707         847 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    3708         847 :     OGRLayer *poDstLayer = poDstDS->GetLayerByName(pszNewLayerName);
    3709         847 :     CPLPopErrorHandler();
    3710         847 :     CPLErrorReset();
    3711             : 
    3712         847 :     int iLayer = -1;
    3713         847 :     if (poDstLayer != nullptr)
    3714             :     {
    3715          53 :         const int nLayerCount = poDstDS->GetLayerCount();
    3716         367 :         for (iLayer = 0; iLayer < nLayerCount; iLayer++)
    3717             :         {
    3718         367 :             OGRLayer *poLayer = poDstDS->GetLayer(iLayer);
    3719         367 :             if (poLayer == poDstLayer)
    3720          53 :                 break;
    3721             :         }
    3722             : 
    3723          53 :         if (iLayer == nLayerCount)
    3724             :             /* should not happen with an ideal driver */
    3725           0 :             poDstLayer = nullptr;
    3726             :     }
    3727             : 
    3728             :     /* -------------------------------------------------------------------- */
    3729             :     /*      If the user requested overwrite, and we have the layer in       */
    3730             :     /*      question we need to delete it now so it will get recreated      */
    3731             :     /*      (overwritten).                                                  */
    3732             :     /* -------------------------------------------------------------------- */
    3733         847 :     if (poDstLayer != nullptr && bOverwrite)
    3734             :     {
    3735             :         /* When using the CARTO driver we don't want to delete the layer if */
    3736             :         /* it's going to be recreated. Instead we mark it to be overwritten */
    3737             :         /* when the new creation is requested */
    3738          12 :         if (poDstDS->GetDriver()->GetMetadataItem(
    3739          24 :                 GDAL_DS_LAYER_CREATIONOPTIONLIST) != nullptr &&
    3740          24 :             strstr(poDstDS->GetDriver()->GetMetadataItem(
    3741          12 :                        GDAL_DS_LAYER_CREATIONOPTIONLIST),
    3742             :                    "CARTODBFY") != nullptr)
    3743             :         {
    3744           0 :             if (pbAddOverwriteLCO)
    3745           0 :                 *pbAddOverwriteLCO = true;
    3746           0 :             if (pbOverwriteActuallyDone)
    3747           0 :                 *pbOverwriteActuallyDone = true;
    3748             :         }
    3749          12 :         else if (poDstDS->DeleteLayer(iLayer) != OGRERR_NONE)
    3750             :         {
    3751           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3752             :                      "DeleteLayer() failed when overwrite requested.");
    3753           0 :             if (pbErrorOccurred)
    3754           0 :                 *pbErrorOccurred = true;
    3755             :         }
    3756             :         else
    3757             :         {
    3758          12 :             if (pbOverwriteActuallyDone)
    3759          12 :                 *pbOverwriteActuallyDone = true;
    3760             :         }
    3761          12 :         poDstLayer = nullptr;
    3762             :     }
    3763             : 
    3764         847 :     return poDstLayer;
    3765             : }
    3766             : 
    3767             : /************************************************************************/
    3768             : /*                          ConvertType()                               */
    3769             : /************************************************************************/
    3770             : 
    3771         962 : static OGRwkbGeometryType ConvertType(GeomTypeConversion eGeomTypeConversion,
    3772             :                                       OGRwkbGeometryType eGType)
    3773             : {
    3774         962 :     OGRwkbGeometryType eRetType = eGType;
    3775             : 
    3776         962 :     if (eGeomTypeConversion == GTC_CONVERT_TO_LINEAR ||
    3777             :         eGeomTypeConversion == GTC_PROMOTE_TO_MULTI_AND_CONVERT_TO_LINEAR)
    3778             :     {
    3779          13 :         eRetType = OGR_GT_GetLinear(eRetType);
    3780             :     }
    3781             : 
    3782         962 :     if (eGeomTypeConversion == GTC_PROMOTE_TO_MULTI ||
    3783             :         eGeomTypeConversion == GTC_PROMOTE_TO_MULTI_AND_CONVERT_TO_LINEAR)
    3784             :     {
    3785          12 :         if (eRetType == wkbTriangle || eRetType == wkbTIN ||
    3786             :             eRetType == wkbPolyhedralSurface)
    3787             :         {
    3788           0 :             eRetType = wkbMultiPolygon;
    3789             :         }
    3790          12 :         else if (!OGR_GT_IsSubClassOf(eRetType, wkbGeometryCollection))
    3791             :         {
    3792           8 :             eRetType = OGR_GT_GetCollection(eRetType);
    3793             :         }
    3794             :     }
    3795             : 
    3796         962 :     if (eGeomTypeConversion == GTC_CONVERT_TO_CURVE)
    3797           2 :         eRetType = OGR_GT_GetCurve(eRetType);
    3798             : 
    3799         962 :     return eRetType;
    3800             : }
    3801             : 
    3802             : /************************************************************************/
    3803             : /*                        DoFieldTypeConversion()                       */
    3804             : /************************************************************************/
    3805             : 
    3806        3159 : static void DoFieldTypeConversion(GDALDataset *poDstDS,
    3807             :                                   OGRFieldDefn &oFieldDefn,
    3808             :                                   CSLConstList papszFieldTypesToString,
    3809             :                                   CSLConstList papszMapFieldType,
    3810             :                                   bool bUnsetFieldWidth, bool bQuiet,
    3811             :                                   bool bForceNullable, bool bUnsetDefault)
    3812             : {
    3813        3159 :     if (papszFieldTypesToString != nullptr)
    3814             :     {
    3815           0 :         CPLString osLookupString;
    3816             :         osLookupString.Printf(
    3817             :             "%s(%s)", OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType()),
    3818           0 :             OGRFieldDefn::GetFieldSubTypeName(oFieldDefn.GetSubType()));
    3819             : 
    3820           0 :         int iIdx = CSLFindString(papszFieldTypesToString, osLookupString);
    3821           0 :         if (iIdx < 0)
    3822           0 :             iIdx = CSLFindString(
    3823             :                 papszFieldTypesToString,
    3824             :                 OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType()));
    3825           0 :         if (iIdx < 0)
    3826           0 :             iIdx = CSLFindString(papszFieldTypesToString, "All");
    3827           0 :         if (iIdx >= 0)
    3828             :         {
    3829           0 :             oFieldDefn.SetSubType(OFSTNone);
    3830           0 :             oFieldDefn.SetType(OFTString);
    3831             :         }
    3832             :     }
    3833        3159 :     else if (papszMapFieldType != nullptr)
    3834             :     {
    3835          28 :         CPLString osLookupString;
    3836             :         osLookupString.Printf(
    3837             :             "%s(%s)", OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType()),
    3838          14 :             OGRFieldDefn::GetFieldSubTypeName(oFieldDefn.GetSubType()));
    3839             : 
    3840             :         const char *pszType =
    3841          14 :             CSLFetchNameValue(papszMapFieldType, osLookupString);
    3842          14 :         if (pszType == nullptr)
    3843          13 :             pszType = CSLFetchNameValue(
    3844             :                 papszMapFieldType,
    3845             :                 OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType()));
    3846          14 :         if (pszType == nullptr)
    3847          10 :             pszType = CSLFetchNameValue(papszMapFieldType, "All");
    3848          14 :         if (pszType != nullptr)
    3849             :         {
    3850             :             int iSubType;
    3851           4 :             int iType = GetFieldType(pszType, &iSubType);
    3852           4 :             if (iType >= 0 && iSubType >= 0)
    3853             :             {
    3854           4 :                 oFieldDefn.SetSubType(OFSTNone);
    3855           4 :                 oFieldDefn.SetType(static_cast<OGRFieldType>(iType));
    3856           4 :                 oFieldDefn.SetSubType(static_cast<OGRFieldSubType>(iSubType));
    3857           4 :                 if (iType == OFTInteger)
    3858           1 :                     oFieldDefn.SetWidth(0);
    3859             :             }
    3860             :         }
    3861             :     }
    3862        3159 :     if (bUnsetFieldWidth)
    3863             :     {
    3864           6 :         oFieldDefn.SetWidth(0);
    3865           6 :         oFieldDefn.SetPrecision(0);
    3866             :     }
    3867        3159 :     if (bForceNullable)
    3868           4 :         oFieldDefn.SetNullable(TRUE);
    3869        3159 :     if (bUnsetDefault)
    3870           2 :         oFieldDefn.SetDefault(nullptr);
    3871             : 
    3872        3159 :     const auto poDstDriver = poDstDS->GetDriver();
    3873             :     const char *pszCreationFieldDataTypes =
    3874             :         poDstDriver
    3875        3159 :             ? poDstDriver->GetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES)
    3876        3159 :             : nullptr;
    3877             :     const char *pszCreationFieldDataSubtypes =
    3878             :         poDstDriver
    3879        3159 :             ? poDstDriver->GetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES)
    3880        3159 :             : nullptr;
    3881        6209 :     if (pszCreationFieldDataTypes &&
    3882        3050 :         strstr(pszCreationFieldDataTypes,
    3883             :                OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType())) == nullptr)
    3884             :     {
    3885          33 :         if (pszCreationFieldDataSubtypes &&
    3886          64 :             (oFieldDefn.GetType() == OFTIntegerList ||
    3887          61 :              oFieldDefn.GetType() == OFTInteger64List ||
    3888          59 :              oFieldDefn.GetType() == OFTRealList ||
    3889          98 :              oFieldDefn.GetType() == OFTStringList) &&
    3890          25 :             strstr(pszCreationFieldDataSubtypes, "JSON"))
    3891             :         {
    3892           5 :             if (!bQuiet)
    3893             :             {
    3894           5 :                 CPLError(
    3895             :                     CE_Warning, CPLE_AppDefined,
    3896             :                     "The output driver does not seem to natively support %s "
    3897             :                     "type for field %s. Converting it to String(JSON) instead. "
    3898             :                     "-mapFieldType can be used to control field type "
    3899             :                     "conversion.",
    3900             :                     OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType()),
    3901             :                     oFieldDefn.GetNameRef());
    3902             :             }
    3903           5 :             oFieldDefn.SetSubType(OFSTNone);
    3904           5 :             oFieldDefn.SetType(OFTString);
    3905           5 :             oFieldDefn.SetSubType(OFSTJSON);
    3906             :         }
    3907          31 :         else if (oFieldDefn.GetType() == OFTInteger64)
    3908             :         {
    3909           6 :             if (!bQuiet)
    3910             :             {
    3911           6 :                 CPLError(
    3912             :                     CE_Warning, CPLE_AppDefined,
    3913             :                     "The output driver does not seem to natively support %s "
    3914             :                     "type for field %s. Converting it to Real instead. "
    3915             :                     "-mapFieldType can be used to control field type "
    3916             :                     "conversion.",
    3917             :                     OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType()),
    3918             :                     oFieldDefn.GetNameRef());
    3919             :             }
    3920           6 :             oFieldDefn.SetType(OFTReal);
    3921             :         }
    3922          25 :         else if (!bQuiet)
    3923             :         {
    3924          25 :             CPLError(
    3925             :                 CE_Warning, CPLE_AppDefined,
    3926             :                 "The output driver does not natively support %s type for "
    3927             :                 "field %s. Misconversion can happen. "
    3928             :                 "-mapFieldType can be used to control field type conversion.",
    3929             :                 OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType()),
    3930             :                 oFieldDefn.GetNameRef());
    3931             :         }
    3932             :     }
    3933        3123 :     else if (!pszCreationFieldDataTypes)
    3934             :     {
    3935             :         // All drivers supporting OFTInteger64 should advertise it theoretically
    3936         109 :         if (oFieldDefn.GetType() == OFTInteger64)
    3937             :         {
    3938           1 :             if (!bQuiet)
    3939             :             {
    3940           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3941             :                          "The output driver does not seem to natively support "
    3942             :                          "%s type "
    3943             :                          "for field %s. Converting it to Real instead. "
    3944             :                          "-mapFieldType can be used to control field type "
    3945             :                          "conversion.",
    3946             :                          OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType()),
    3947             :                          oFieldDefn.GetNameRef());
    3948             :             }
    3949           1 :             oFieldDefn.SetType(OFTReal);
    3950             :         }
    3951             :     }
    3952        3159 : }
    3953             : 
    3954             : /************************************************************************/
    3955             : /*                        GetArrowGeomFieldIndex()                      */
    3956             : /************************************************************************/
    3957             : 
    3958          22 : static int GetArrowGeomFieldIndex(const struct ArrowSchema *psLayerSchema,
    3959             :                                   const char *pszFieldName)
    3960             : {
    3961          22 :     if (strcmp(psLayerSchema->format, "+s") == 0)  // struct
    3962             :     {
    3963          80 :         for (int i = 0; i < psLayerSchema->n_children; ++i)
    3964             :         {
    3965          80 :             const auto psSchema = psLayerSchema->children[i];
    3966          80 :             if (strcmp(psSchema->format, "z") == 0)  // binary
    3967             :             {
    3968          22 :                 if (strcmp(psSchema->name, pszFieldName) == 0)
    3969             :                 {
    3970          22 :                     return i;
    3971             :                 }
    3972             :                 else
    3973             :                 {
    3974             :                     // Check if ARROW:extension:name = ogc.wkb or geoarrow.wkb
    3975           0 :                     const char *pabyMetadata = psSchema->metadata;
    3976           0 :                     if (pabyMetadata)
    3977             :                     {
    3978             :                         const auto oMetadata =
    3979           0 :                             OGRParseArrowMetadata(pabyMetadata);
    3980           0 :                         auto oIter = oMetadata.find(ARROW_EXTENSION_NAME_KEY);
    3981           0 :                         if (oIter != oMetadata.end() &&
    3982           0 :                             (oIter->second == EXTENSION_NAME_OGC_WKB ||
    3983           0 :                              oIter->second == EXTENSION_NAME_GEOARROW_WKB))
    3984             :                         {
    3985           0 :                             return i;
    3986             :                         }
    3987             :                     }
    3988             :                 }
    3989             :             }
    3990             :         }
    3991             :     }
    3992           0 :     return -1;
    3993             : }
    3994             : 
    3995             : /************************************************************************/
    3996             : /*                        BuildGetArrowStreamOptions()                  */
    3997             : /************************************************************************/
    3998             : 
    3999             : static CPLStringList
    4000         142 : BuildGetArrowStreamOptions(const GDALVectorTranslateOptions *psOptions,
    4001             :                            bool bPreserveFID)
    4002             : {
    4003         142 :     CPLStringList aosOptionsGetArrowStream;
    4004         142 :     aosOptionsGetArrowStream.SetNameValue("SILENCE_GET_SCHEMA_ERROR", "YES");
    4005         142 :     aosOptionsGetArrowStream.SetNameValue("GEOMETRY_ENCODING", "WKB");
    4006         142 :     if (!bPreserveFID)
    4007         126 :         aosOptionsGetArrowStream.SetNameValue("INCLUDE_FID", "NO");
    4008         142 :     if (psOptions->nLimit >= 0)
    4009             :     {
    4010             :         aosOptionsGetArrowStream.SetNameValue(
    4011             :             "MAX_FEATURES_IN_BATCH",
    4012             :             CPLSPrintf(CPL_FRMT_GIB,
    4013           2 :                        std::min<GIntBig>(psOptions->nLimit,
    4014           2 :                                          (psOptions->nGroupTransactions > 0
    4015           4 :                                               ? psOptions->nGroupTransactions
    4016           2 :                                               : 65536))));
    4017             :     }
    4018         140 :     else if (psOptions->nGroupTransactions > 0)
    4019             :     {
    4020             :         aosOptionsGetArrowStream.SetNameValue(
    4021             :             "MAX_FEATURES_IN_BATCH",
    4022         140 :             CPLSPrintf("%d", psOptions->nGroupTransactions));
    4023             :     }
    4024         142 :     return aosOptionsGetArrowStream;
    4025             : }
    4026             : 
    4027             : /************************************************************************/
    4028             : /*                 SetupTargetLayer::CanUseWriteArrowBatch()            */
    4029             : /************************************************************************/
    4030             : 
    4031         843 : bool SetupTargetLayer::CanUseWriteArrowBatch(
    4032             :     OGRLayer *poSrcLayer, OGRLayer *poDstLayer, bool bJustCreatedLayer,
    4033             :     const GDALVectorTranslateOptions *psOptions, bool bPreserveFID,
    4034             :     bool &bError, OGRArrowArrayStream &streamSrc)
    4035             : {
    4036         843 :     bError = false;
    4037             : 
    4038             :     // Check if we can use the Arrow interface to get and write features
    4039             :     // as it will be faster if the input driver has a fast
    4040             :     // implementation of GetArrowStream().
    4041             :     // We also can only do that only if using ogr2ogr without options that
    4042             :     // alter features.
    4043             :     // OGR2OGR_USE_ARROW_API config option is mostly for testing purposes
    4044             :     // or as a safety belt if things turned bad...
    4045         843 :     bool bUseWriteArrowBatch = false;
    4046         843 :     if (((poSrcLayer->TestCapability(OLCFastGetArrowStream) &&
    4047             :           // As we don't control the input array size when the input or output
    4048             :           // drivers are Arrow/Parquet (as they don't use the generic
    4049             :           // implementation), we can't guarantee that ROW_GROUP_SIZE/BATCH_SIZE
    4050             :           // layer creation options will be honored.
    4051         150 :           !psOptions->aosLCO.FetchNameValue("ROW_GROUP_SIZE") &&
    4052         148 :           !psOptions->aosLCO.FetchNameValue("BATCH_SIZE") &&
    4053         145 :           CPLTestBool(CPLGetConfigOption("OGR2OGR_USE_ARROW_API", "YES"))) ||
    4054         698 :          CPLTestBool(CPLGetConfigOption("OGR2OGR_USE_ARROW_API", "NO"))) &&
    4055         150 :         !psOptions->bSkipFailures && !psOptions->poClipSrc &&
    4056         147 :         !psOptions->poClipDst && psOptions->oGCPs.nGCPCount == 0 &&
    4057         144 :         !psOptions->bWrapDateline && !m_papszSelFields &&
    4058         144 :         !m_bAddMissingFields && m_eGType == GEOMTYPE_UNCHANGED &&
    4059         144 :         psOptions->eGeomOp == GEOMOP_NONE &&
    4060         144 :         m_eGeomTypeConversion == GTC_DEFAULT && m_nCoordDim < 0 &&
    4061         144 :         !m_papszFieldTypesToString && !m_papszMapFieldType &&
    4062         144 :         !m_bUnsetFieldWidth && !m_bExplodeCollections && !m_pszZField &&
    4063         143 :         m_bExactFieldNameMatch && !m_bForceNullable && !m_bResolveDomains &&
    4064         143 :         !m_bUnsetDefault && psOptions->nFIDToFetch == OGRNullFID &&
    4065         143 :         psOptions->dfXYRes == OGRGeomCoordinatePrecision::UNKNOWN &&
    4066        1686 :         !psOptions->bMakeValid && !psOptions->bSkipInvalidGeom)
    4067             :     {
    4068         143 :         if (psOptions->bTransform)
    4069             :         {
    4070             :             // To simplify implementation for now
    4071          24 :             if (poSrcLayer->GetLayerDefn()->GetGeomFieldCount() != 1 ||
    4072          12 :                 poDstLayer->GetLayerDefn()->GetGeomFieldCount() != 1)
    4073             :             {
    4074           1 :                 return false;
    4075             :             }
    4076          12 :             auto poSrcSRS = m_poUserSourceSRS ? m_poUserSourceSRS
    4077          11 :                                               : poSrcLayer->GetLayerDefn()
    4078          11 :                                                     ->GetGeomFieldDefn(0)
    4079          11 :                                                     ->GetSpatialRef();
    4080          24 :             if (!poSrcSRS ||
    4081          12 :                 !OGRGeometryFactory::isTransformWithOptionsRegularTransform(
    4082             :                     poSrcSRS, m_poOutputSRS, nullptr))
    4083             :             {
    4084           1 :                 return false;
    4085             :             }
    4086             :         }
    4087             : 
    4088             :         const CPLStringList aosGetArrowStreamOptions(
    4089         142 :             BuildGetArrowStreamOptions(psOptions, bPreserveFID));
    4090         142 :         if (poSrcLayer->GetArrowStream(streamSrc.get(),
    4091         142 :                                        aosGetArrowStreamOptions.List()))
    4092             :         {
    4093             :             struct ArrowSchema schemaSrc;
    4094         142 :             if (streamSrc.get_schema(&schemaSrc) == 0)
    4095             :             {
    4096         153 :                 if (psOptions->bTransform &&
    4097          11 :                     GetArrowGeomFieldIndex(&schemaSrc,
    4098          11 :                                            poSrcLayer->GetGeometryColumn()) < 0)
    4099             :                 {
    4100           0 :                     schemaSrc.release(&schemaSrc);
    4101           0 :                     streamSrc.clear();
    4102           0 :                     return false;
    4103             :                 }
    4104             : 
    4105         142 :                 std::string osErrorMsg;
    4106         142 :                 if (poDstLayer->IsArrowSchemaSupported(&schemaSrc, nullptr,
    4107         142 :                                                        osErrorMsg))
    4108             :                 {
    4109             :                     const OGRFeatureDefn *poSrcFDefn =
    4110         142 :                         poSrcLayer->GetLayerDefn();
    4111             :                     const OGRFeatureDefn *poDstFDefn =
    4112         142 :                         poDstLayer->GetLayerDefn();
    4113         140 :                     if (bJustCreatedLayer && poDstFDefn &&
    4114         422 :                         poDstFDefn->GetFieldCount() == 0 &&
    4115         140 :                         poDstFDefn->GetGeomFieldCount() ==
    4116         140 :                             poSrcFDefn->GetGeomFieldCount())
    4117             :                     {
    4118             :                         // Create output fields using CreateFieldFromArrowSchema()
    4119        1185 :                         for (int i = 0; i < schemaSrc.n_children; ++i)
    4120             :                         {
    4121        1045 :                             const char *pszFieldName =
    4122        1045 :                                 schemaSrc.children[i]->name;
    4123             : 
    4124             :                             const auto iSrcField =
    4125        1045 :                                 poSrcFDefn->GetFieldIndex(pszFieldName);
    4126        1045 :                             if (iSrcField >= 0)
    4127             :                             {
    4128             :                                 const auto poSrcFieldDefn =
    4129         881 :                                     poSrcFDefn->GetFieldDefn(iSrcField);
    4130             :                                 // Create field domain in output dataset if not already existing.
    4131             :                                 const std::string osDomainName(
    4132        1762 :                                     poSrcFieldDefn->GetDomainName());
    4133         881 :                                 if (!osDomainName.empty())
    4134             :                                 {
    4135          33 :                                     if (m_poDstDS->TestCapability(
    4136          22 :                                             ODsCAddFieldDomain) &&
    4137          11 :                                         m_poDstDS->GetFieldDomain(
    4138          11 :                                             osDomainName) == nullptr)
    4139             :                                     {
    4140             :                                         const auto poSrcDomain =
    4141          22 :                                             m_poSrcDS->GetFieldDomain(
    4142          11 :                                                 osDomainName);
    4143          11 :                                         if (poSrcDomain)
    4144             :                                         {
    4145          22 :                                             std::string failureReason;
    4146          11 :                                             if (!m_poDstDS->AddFieldDomain(
    4147          22 :                                                     std::unique_ptr<
    4148             :                                                         OGRFieldDomain>(
    4149          11 :                                                         poSrcDomain->Clone()),
    4150          11 :                                                     failureReason))
    4151             :                                             {
    4152           0 :                                                 CPLDebug("OGR2OGR",
    4153             :                                                          "Cannot create domain "
    4154             :                                                          "%s: %s",
    4155             :                                                          osDomainName.c_str(),
    4156             :                                                          failureReason.c_str());
    4157             :                                             }
    4158             :                                         }
    4159             :                                         else
    4160             :                                         {
    4161           0 :                                             CPLDebug("OGR2OGR",
    4162             :                                                      "Cannot find domain %s in "
    4163             :                                                      "source dataset",
    4164             :                                                      osDomainName.c_str());
    4165             :                                         }
    4166             :                                     }
    4167             :                                 }
    4168             :                             }
    4169             : 
    4170        3135 :                             if (!EQUAL(pszFieldName, "OGC_FID") &&
    4171        1045 :                                 !EQUAL(pszFieldName, "wkb_geometry") &&
    4172        1042 :                                 !EQUAL(pszFieldName,
    4173        1026 :                                        poSrcLayer->GetFIDColumn()) &&
    4174        1026 :                                 poSrcFDefn->GetGeomFieldIndex(pszFieldName) <
    4175        2090 :                                     0 &&
    4176         890 :                                 !poDstLayer->CreateFieldFromArrowSchema(
    4177         890 :                                     schemaSrc.children[i], nullptr))
    4178             :                             {
    4179           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
    4180             :                                          "Cannot create field %s",
    4181             :                                          pszFieldName);
    4182           0 :                                 schemaSrc.release(&schemaSrc);
    4183           0 :                                 streamSrc.clear();
    4184           0 :                                 return false;
    4185             :                             }
    4186             :                         }
    4187         140 :                         bUseWriteArrowBatch = true;
    4188             :                     }
    4189           2 :                     else if (!bJustCreatedLayer)
    4190             :                     {
    4191             :                         // If the layer already exist, get its schema, and
    4192             :                         // check that it looks to be the same as the source
    4193             :                         // one
    4194             :                         struct ArrowArrayStream streamDst;
    4195           2 :                         if (poDstLayer->GetArrowStream(
    4196           2 :                                 &streamDst, aosGetArrowStreamOptions.List()))
    4197             :                         {
    4198             :                             struct ArrowSchema schemaDst;
    4199           2 :                             if (streamDst.get_schema(&streamDst, &schemaDst) ==
    4200             :                                 0)
    4201             :                             {
    4202           2 :                                 if (schemaDst.n_children ==
    4203           2 :                                     schemaSrc.n_children)
    4204             :                                 {
    4205           2 :                                     bUseWriteArrowBatch = true;
    4206             :                                 }
    4207           2 :                                 schemaDst.release(&schemaDst);
    4208             :                             }
    4209           2 :                             streamDst.release(&streamDst);
    4210             :                         }
    4211             :                     }
    4212         142 :                     if (bUseWriteArrowBatch)
    4213             :                     {
    4214         142 :                         CPLDebug("OGR2OGR", "Using WriteArrowBatch()");
    4215             :                     }
    4216             :                 }
    4217             :                 else
    4218             :                 {
    4219           0 :                     CPLDebug("OGR2OGR",
    4220             :                              "Cannot use WriteArrowBatch() because "
    4221             :                              "input layer schema is not supported by output "
    4222             :                              "layer: %s",
    4223             :                              osErrorMsg.c_str());
    4224             :                 }
    4225         142 :                 schemaSrc.release(&schemaSrc);
    4226             :             }
    4227         142 :             if (!bUseWriteArrowBatch)
    4228           0 :                 streamSrc.clear();
    4229             :         }
    4230             :     }
    4231         842 :     return bUseWriteArrowBatch;
    4232             : }
    4233             : 
    4234             : /************************************************************************/
    4235             : /*                   SetupTargetLayer::Setup()                          */
    4236             : /************************************************************************/
    4237             : 
    4238             : std::unique_ptr<TargetLayerInfo>
    4239         847 : SetupTargetLayer::Setup(OGRLayer *poSrcLayer, const char *pszNewLayerName,
    4240             :                         GDALVectorTranslateOptions *psOptions,
    4241             :                         GIntBig &nTotalEventsDone)
    4242             : {
    4243         847 :     int eGType = m_eGType;
    4244         847 :     bool bPreserveFID = m_bPreserveFID;
    4245         847 :     bool bAppend = m_bAppend;
    4246             : 
    4247         847 :     if (pszNewLayerName == nullptr)
    4248         816 :         pszNewLayerName = poSrcLayer->GetName();
    4249             : 
    4250             :     /* -------------------------------------------------------------------- */
    4251             :     /*      Get other info.                                                 */
    4252             :     /* -------------------------------------------------------------------- */
    4253         847 :     OGRFeatureDefn *poSrcFDefn = poSrcLayer->GetLayerDefn();
    4254             : 
    4255             :     /* -------------------------------------------------------------------- */
    4256             :     /*      Find requested geometry fields.                                 */
    4257             :     /* -------------------------------------------------------------------- */
    4258        1694 :     std::vector<int> anRequestedGeomFields;
    4259         847 :     const int nSrcGeomFieldCount = poSrcFDefn->GetGeomFieldCount();
    4260         847 :     if (m_bSelFieldsSet && !bAppend)
    4261             :     {
    4262          40 :         for (int iField = 0; m_papszSelFields && m_papszSelFields[iField];
    4263             :              iField++)
    4264             :         {
    4265          25 :             int iSrcField = poSrcFDefn->GetFieldIndex(m_papszSelFields[iField]);
    4266          25 :             if (iSrcField >= 0)
    4267             :             {
    4268             :                 /* do nothing */
    4269             :             }
    4270             :             else
    4271             :             {
    4272           3 :                 iSrcField =
    4273           3 :                     poSrcFDefn->GetGeomFieldIndex(m_papszSelFields[iField]);
    4274           3 :                 if (iSrcField >= 0)
    4275             :                 {
    4276           3 :                     anRequestedGeomFields.push_back(iSrcField);
    4277             :                 }
    4278             :                 else
    4279             :                 {
    4280           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    4281             :                              "Field '%s' not found in source layer.",
    4282           0 :                              m_papszSelFields[iField]);
    4283           0 :                     if (!psOptions->bSkipFailures)
    4284           0 :                         return nullptr;
    4285             :                 }
    4286             :             }
    4287             :         }
    4288             : 
    4289          16 :         if (anRequestedGeomFields.size() > 1 &&
    4290           1 :             !m_poDstDS->TestCapability(ODsCCreateGeomFieldAfterCreateLayer))
    4291             :         {
    4292           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4293             :                      "Several geometry fields requested, but output "
    4294             :                      "datasource does not support multiple geometry "
    4295             :                      "fields.");
    4296           0 :             if (!psOptions->bSkipFailures)
    4297           0 :                 return nullptr;
    4298             :             else
    4299           0 :                 anRequestedGeomFields.resize(0);
    4300             :         }
    4301             :     }
    4302             : 
    4303         847 :     const OGRSpatialReference *poOutputSRS = m_poOutputSRS;
    4304         847 :     if (poOutputSRS == nullptr && !m_bNullifyOutputSRS)
    4305             :     {
    4306         701 :         if (nSrcGeomFieldCount == 1 || anRequestedGeomFields.empty())
    4307         699 :             poOutputSRS = poSrcLayer->GetSpatialRef();
    4308           2 :         else if (anRequestedGeomFields.size() == 1)
    4309             :         {
    4310           1 :             int iSrcGeomField = anRequestedGeomFields[0];
    4311             :             poOutputSRS =
    4312           1 :                 poSrcFDefn->GetGeomFieldDefn(iSrcGeomField)->GetSpatialRef();
    4313             :         }
    4314             :     }
    4315             : 
    4316         847 :     int iSrcZField = -1;
    4317         847 :     if (m_pszZField != nullptr)
    4318             :     {
    4319           4 :         iSrcZField = poSrcFDefn->GetFieldIndex(m_pszZField);
    4320           4 :         if (iSrcZField < 0)
    4321             :         {
    4322           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    4323             :                      "zfield '%s' does not exist in layer %s", m_pszZField,
    4324           1 :                      poSrcLayer->GetName());
    4325             :         }
    4326             :     }
    4327             : 
    4328             :     /* -------------------------------------------------------------------- */
    4329             :     /*      Find the layer.                                                 */
    4330             :     /* -------------------------------------------------------------------- */
    4331             : 
    4332             :     bool bErrorOccurred;
    4333             :     bool bOverwriteActuallyDone;
    4334             :     bool bAddOverwriteLCO;
    4335        1694 :     OGRLayer *poDstLayer = GetLayerAndOverwriteIfNecessary(
    4336         847 :         m_poDstDS, pszNewLayerName, m_bOverwrite, &bErrorOccurred,
    4337             :         &bOverwriteActuallyDone, &bAddOverwriteLCO);
    4338         847 :     const bool bJustCreatedLayer = (poDstLayer == nullptr);
    4339         847 :     if (bErrorOccurred)
    4340           0 :         return nullptr;
    4341             : 
    4342             :     /* -------------------------------------------------------------------- */
    4343             :     /*      If the layer does not exist, then create it.                    */
    4344             :     /* -------------------------------------------------------------------- */
    4345         847 :     if (poDstLayer == nullptr)
    4346             :     {
    4347         806 :         if (!m_poDstDS->TestCapability(ODsCCreateLayer))
    4348             :         {
    4349           0 :             CPLError(
    4350             :                 CE_Failure, CPLE_AppDefined,
    4351             :                 "Layer '%s' does not already exist in the output dataset, and "
    4352             :                 "cannot be created by the output driver.",
    4353             :                 pszNewLayerName);
    4354           4 :             return nullptr;
    4355             :         }
    4356             : 
    4357         806 :         bool bForceGType = (eGType != GEOMTYPE_UNCHANGED);
    4358         806 :         if (!bForceGType)
    4359             :         {
    4360         790 :             if (anRequestedGeomFields.empty())
    4361             :             {
    4362         788 :                 eGType = poSrcFDefn->GetGeomType();
    4363             :             }
    4364           2 :             else if (anRequestedGeomFields.size() == 1)
    4365             :             {
    4366           1 :                 int iSrcGeomField = anRequestedGeomFields[0];
    4367           1 :                 eGType = poSrcFDefn->GetGeomFieldDefn(iSrcGeomField)->GetType();
    4368             :             }
    4369             :             else
    4370             :             {
    4371           1 :                 eGType = wkbNone;
    4372             :             }
    4373             : 
    4374             :             bool bHasZ =
    4375         790 :                 CPL_TO_BOOL(wkbHasZ(static_cast<OGRwkbGeometryType>(eGType)));
    4376         790 :             eGType = ConvertType(m_eGeomTypeConversion,
    4377             :                                  static_cast<OGRwkbGeometryType>(eGType));
    4378             : 
    4379         790 :             if (m_bExplodeCollections)
    4380             :             {
    4381          12 :                 const OGRwkbGeometryType eFGType = wkbFlatten(eGType);
    4382          12 :                 if (eFGType == wkbMultiPoint)
    4383             :                 {
    4384           1 :                     eGType = wkbPoint;
    4385             :                 }
    4386          11 :                 else if (eFGType == wkbMultiLineString)
    4387             :                 {
    4388           0 :                     eGType = wkbLineString;
    4389             :                 }
    4390          11 :                 else if (eFGType == wkbMultiPolygon)
    4391             :                 {
    4392           0 :                     eGType = wkbPolygon;
    4393             :                 }
    4394          11 :                 else if (eFGType == wkbGeometryCollection ||
    4395          11 :                          eFGType == wkbMultiCurve || eFGType == wkbMultiSurface)
    4396             :                 {
    4397           0 :                     eGType = wkbUnknown;
    4398             :                 }
    4399             :             }
    4400             : 
    4401         790 :             if (bHasZ || (iSrcZField >= 0 && eGType != wkbNone))
    4402         109 :                 eGType = wkbSetZ(static_cast<OGRwkbGeometryType>(eGType));
    4403             :         }
    4404             : 
    4405         806 :         eGType = ForceCoordDimension(eGType, m_nCoordDim);
    4406             : 
    4407         806 :         CPLErrorReset();
    4408             : 
    4409         806 :         char **papszLCOTemp = CSLDuplicate(m_papszLCO);
    4410             :         const char *pszDestCreationOptions =
    4411         806 :             m_poDstDS->GetDriver()->GetMetadataItem(
    4412         806 :                 GDAL_DS_LAYER_CREATIONOPTIONLIST);
    4413             : 
    4414         806 :         int eGCreateLayerType = eGType;
    4415         817 :         if (anRequestedGeomFields.empty() && nSrcGeomFieldCount > 1 &&
    4416          11 :             m_poDstDS->TestCapability(ODsCCreateGeomFieldAfterCreateLayer))
    4417             :         {
    4418          10 :             eGCreateLayerType = wkbNone;
    4419             :         }
    4420             :         // If the source layer has a single geometry column that is not nullable
    4421             :         // and that ODsCCreateGeomFieldAfterCreateLayer is available, use it
    4422             :         // so as to be able to set the not null constraint (if the driver
    4423             :         // supports it) and that the output driver has no GEOMETRY_NULLABLE
    4424             :         // layer creation option. Same if the source geometry column has a non
    4425             :         // empty name that is not overridden, and that the output driver has no
    4426             :         // GEOMETRY_NAME layer creation option, but no LAUNDER option (if
    4427             :         // laundering is available, then we might want to launder the geometry
    4428             :         // column name as well)
    4429         640 :         else if (eGType != wkbNone && anRequestedGeomFields.empty() &&
    4430         638 :                  nSrcGeomFieldCount == 1 &&
    4431         638 :                  m_poDstDS->TestCapability(
    4432        2229 :                      ODsCCreateGeomFieldAfterCreateLayer) &&
    4433         155 :                  ((!poSrcFDefn->GetGeomFieldDefn(0)->IsNullable() &&
    4434           2 :                    CSLFetchNameValue(m_papszLCO, "GEOMETRY_NULLABLE") ==
    4435           2 :                        nullptr &&
    4436           2 :                    (pszDestCreationOptions == nullptr ||
    4437           2 :                     strstr(pszDestCreationOptions, "GEOMETRY_NULLABLE") !=
    4438           0 :                         nullptr) &&
    4439           0 :                    !m_bForceNullable) ||
    4440         155 :                   (poSrcLayer->GetGeometryColumn() != nullptr &&
    4441         155 :                    CSLFetchNameValue(m_papszLCO, "GEOMETRY_NAME") == nullptr &&
    4442         155 :                    !EQUAL(poSrcLayer->GetGeometryColumn(), "") &&
    4443          35 :                    (pszDestCreationOptions == nullptr ||
    4444          35 :                     strstr(pszDestCreationOptions, "GEOMETRY_NAME") ==
    4445           8 :                         nullptr ||
    4446           8 :                     strstr(pszDestCreationOptions, "LAUNDER") != nullptr) &&
    4447          35 :                    poSrcFDefn->GetFieldIndex(poSrcLayer->GetGeometryColumn()) <
    4448             :                        0)))
    4449             :         {
    4450          35 :             anRequestedGeomFields.push_back(0);
    4451          35 :             eGCreateLayerType = wkbNone;
    4452             :         }
    4453         762 :         else if (anRequestedGeomFields.size() == 1 &&
    4454           1 :                  m_poDstDS->TestCapability(ODsCCreateGeomFieldAfterCreateLayer))
    4455             :         {
    4456           0 :             eGCreateLayerType = wkbNone;
    4457             :         }
    4458             : 
    4459         806 :         OGRGeomCoordinatePrecision oCoordPrec;
    4460         806 :         std::string osGeomFieldName;
    4461         806 :         bool bGeomFieldNullable = true;
    4462             : 
    4463             :         {
    4464         806 :             int iSrcGeomField = -1;
    4465         972 :             if (anRequestedGeomFields.empty() &&
    4466         166 :                 (nSrcGeomFieldCount == 1 ||
    4467         166 :                  (!m_poDstDS->TestCapability(
    4468         200 :                       ODsCCreateGeomFieldAfterCreateLayer) &&
    4469             :                   nSrcGeomFieldCount > 1)))
    4470             :             {
    4471         604 :                 iSrcGeomField = 0;
    4472             :             }
    4473         202 :             else if (anRequestedGeomFields.size() == 1)
    4474             :             {
    4475          36 :                 iSrcGeomField = anRequestedGeomFields[0];
    4476             :             }
    4477             : 
    4478         806 :             if (iSrcGeomField >= 0)
    4479             :             {
    4480             :                 const auto poSrcGeomFieldDefn =
    4481         640 :                     poSrcFDefn->GetGeomFieldDefn(iSrcGeomField);
    4482         640 :                 if (!psOptions->bUnsetCoordPrecision)
    4483             :                 {
    4484         639 :                     oCoordPrec = poSrcGeomFieldDefn->GetCoordinatePrecision()
    4485        1278 :                                      .ConvertToOtherSRS(
    4486         639 :                                          poSrcGeomFieldDefn->GetSpatialRef(),
    4487         639 :                                          poOutputSRS);
    4488             :                 }
    4489             : 
    4490             :                 bGeomFieldNullable =
    4491         640 :                     CPL_TO_BOOL(poSrcGeomFieldDefn->IsNullable());
    4492             : 
    4493         640 :                 const char *pszGFldName = poSrcGeomFieldDefn->GetNameRef();
    4494         837 :                 if (pszGFldName != nullptr && !EQUAL(pszGFldName, "") &&
    4495         197 :                     poSrcFDefn->GetFieldIndex(pszGFldName) < 0)
    4496             :                 {
    4497         196 :                     osGeomFieldName = pszGFldName;
    4498             : 
    4499             :                     // Use source geometry field name as much as possible
    4500         196 :                     if (eGType != wkbNone && pszDestCreationOptions &&
    4501         196 :                         strstr(pszDestCreationOptions, "GEOMETRY_NAME") !=
    4502         392 :                             nullptr &&
    4503         156 :                         CSLFetchNameValue(m_papszLCO, "GEOMETRY_NAME") ==
    4504             :                             nullptr)
    4505             :                     {
    4506         156 :                         papszLCOTemp = CSLSetNameValue(
    4507             :                             papszLCOTemp, "GEOMETRY_NAME", pszGFldName);
    4508             :                     }
    4509             :                 }
    4510             :             }
    4511             :         }
    4512             : 
    4513             :         // If the source feature first geometry column is not nullable
    4514             :         // and that GEOMETRY_NULLABLE creation option is available, use it
    4515             :         // so as to be able to set the not null constraint (if the driver
    4516             :         // supports it)
    4517         650 :         if (eGType != wkbNone && anRequestedGeomFields.empty() &&
    4518         614 :             nSrcGeomFieldCount >= 1 &&
    4519         614 :             !poSrcFDefn->GetGeomFieldDefn(0)->IsNullable() &&
    4520           0 :             pszDestCreationOptions != nullptr &&
    4521           0 :             strstr(pszDestCreationOptions, "GEOMETRY_NULLABLE") != nullptr &&
    4522        1456 :             CSLFetchNameValue(m_papszLCO, "GEOMETRY_NULLABLE") == nullptr &&
    4523           0 :             !m_bForceNullable)
    4524             :         {
    4525           0 :             bGeomFieldNullable = false;
    4526             :             papszLCOTemp =
    4527           0 :                 CSLSetNameValue(papszLCOTemp, "GEOMETRY_NULLABLE", "NO");
    4528           0 :             CPLDebug("GDALVectorTranslate", "Using GEOMETRY_NULLABLE=NO");
    4529             :         }
    4530             : 
    4531         806 :         if (psOptions->dfXYRes != OGRGeomCoordinatePrecision::UNKNOWN)
    4532             :         {
    4533           7 :             if (m_poDstDS->GetDriver()->GetMetadataItem(
    4534           8 :                     GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION) == nullptr &&
    4535           1 :                 !OGRGeometryFactory::haveGEOS())
    4536             :             {
    4537           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    4538             :                          "-xyRes specified, but driver does not expose the "
    4539             :                          "DCAP_HONOR_GEOM_COORDINATE_PRECISION capability, "
    4540             :                          "and this build has no GEOS support");
    4541             :             }
    4542             : 
    4543           7 :             oCoordPrec.dfXYResolution = psOptions->dfXYRes;
    4544           7 :             if (!psOptions->osXYResUnit.empty())
    4545             :             {
    4546           5 :                 if (!poOutputSRS)
    4547             :                 {
    4548           1 :                     CSLDestroy(papszLCOTemp);
    4549           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    4550             :                              "Unit suffix for -xyRes cannot be used with an "
    4551             :                              "unknown destination SRS");
    4552           1 :                     return nullptr;
    4553             :                 }
    4554             : 
    4555           4 :                 if (psOptions->osXYResUnit == "mm")
    4556             :                 {
    4557           1 :                     oCoordPrec.dfXYResolution *= 1e-3;
    4558             :                 }
    4559           3 :                 else if (psOptions->osXYResUnit == "deg")
    4560             :                 {
    4561             :                     double dfFactorDegToMeter =
    4562           2 :                         poOutputSRS->GetSemiMajor(nullptr) * M_PI / 180;
    4563           2 :                     oCoordPrec.dfXYResolution *= dfFactorDegToMeter;
    4564             :                 }
    4565             :                 else
    4566             :                 {
    4567             :                     // Checked at argument parsing time
    4568           1 :                     CPLAssert(psOptions->osXYResUnit == "m");
    4569             :                 }
    4570             : 
    4571           4 :                 OGRGeomCoordinatePrecision tmp;
    4572           4 :                 tmp.SetFromMeter(poOutputSRS, oCoordPrec.dfXYResolution, 0, 0);
    4573           4 :                 oCoordPrec.dfXYResolution = tmp.dfXYResolution;
    4574             :             }
    4575             :         }
    4576             : 
    4577         805 :         if (psOptions->dfZRes != OGRGeomCoordinatePrecision::UNKNOWN)
    4578             :         {
    4579           4 :             if (m_poDstDS->GetDriver()->GetMetadataItem(
    4580           4 :                     GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION) == nullptr)
    4581             :             {
    4582           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    4583             :                          "-zRes specified, but driver does not expose the "
    4584             :                          "DCAP_HONOR_GEOM_COORDINATE_PRECISION capability");
    4585             :             }
    4586             : 
    4587           4 :             oCoordPrec.dfZResolution = psOptions->dfZRes;
    4588           4 :             if (!psOptions->osZResUnit.empty())
    4589             :             {
    4590           3 :                 if (!poOutputSRS)
    4591             :                 {
    4592           1 :                     CSLDestroy(papszLCOTemp);
    4593           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    4594             :                              "Unit suffix for -zRes cannot be used with an "
    4595             :                              "unknown destination SRS");
    4596           1 :                     return nullptr;
    4597             :                 }
    4598             : 
    4599           2 :                 if (psOptions->osZResUnit == "mm")
    4600             :                 {
    4601           1 :                     oCoordPrec.dfZResolution *= 1e-3;
    4602             :                 }
    4603             :                 else
    4604             :                 {
    4605             :                     // Checked at argument parsing time
    4606           1 :                     CPLAssert(psOptions->osZResUnit == "m");
    4607             :                 }
    4608             : 
    4609           2 :                 OGRGeomCoordinatePrecision tmp;
    4610           2 :                 tmp.SetFromMeter(poOutputSRS, 0, oCoordPrec.dfZResolution, 0);
    4611           2 :                 oCoordPrec.dfZResolution = tmp.dfZResolution;
    4612             :             }
    4613             :         }
    4614             : 
    4615         804 :         if (psOptions->dfMRes != OGRGeomCoordinatePrecision::UNKNOWN)
    4616             :         {
    4617           3 :             if (m_poDstDS->GetDriver()->GetMetadataItem(
    4618           3 :                     GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION) == nullptr)
    4619             :             {
    4620           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    4621             :                          "-mRes specified, but driver does not expose the "
    4622             :                          "DCAP_HONOR_GEOM_COORDINATE_PRECISION capability");
    4623             :             }
    4624             : 
    4625           3 :             oCoordPrec.dfMResolution = psOptions->dfMRes;
    4626             :         }
    4627             : 
    4628             :         // Force FID column as 64 bit if the source feature has a 64 bit FID,
    4629             :         // the target driver supports 64 bit FID and the user didn't set it
    4630             :         // manually.
    4631         804 :         if (poSrcLayer->GetMetadataItem(OLMD_FID64) != nullptr &&
    4632           1 :             EQUAL(poSrcLayer->GetMetadataItem(OLMD_FID64), "YES") &&
    4633           1 :             pszDestCreationOptions &&
    4634         805 :             strstr(pszDestCreationOptions, "FID64") != nullptr &&
    4635           0 :             CSLFetchNameValue(m_papszLCO, "FID64") == nullptr)
    4636             :         {
    4637           0 :             papszLCOTemp = CSLSetNameValue(papszLCOTemp, "FID64", "YES");
    4638           0 :             CPLDebug("GDALVectorTranslate", "Using FID64=YES");
    4639             :         }
    4640             : 
    4641             :         // If output driver supports FID layer creation option, set it with
    4642             :         // the FID column name of the source layer
    4643         802 :         if (!m_bUnsetFid && !bAppend && poSrcLayer->GetFIDColumn() != nullptr &&
    4644         801 :             !EQUAL(poSrcLayer->GetFIDColumn(), "") &&
    4645          39 :             pszDestCreationOptions != nullptr &&
    4646          39 :             (strstr(pszDestCreationOptions, "='FID'") != nullptr ||
    4647        1608 :              strstr(pszDestCreationOptions, "=\"FID\"") != nullptr) &&
    4648          35 :             CSLFetchNameValue(m_papszLCO, "FID") == nullptr)
    4649             :         {
    4650          35 :             papszLCOTemp = CSLSetNameValue(papszLCOTemp, "FID",
    4651          35 :                                            poSrcLayer->GetFIDColumn());
    4652          35 :             if (!psOptions->bExplodeCollections)
    4653             :             {
    4654          34 :                 CPLDebug("GDALVectorTranslate",
    4655             :                          "Using FID=%s and -preserve_fid",
    4656          34 :                          poSrcLayer->GetFIDColumn());
    4657          34 :                 bPreserveFID = true;
    4658             :             }
    4659             :             else
    4660             :             {
    4661           1 :                 CPLDebug("GDALVectorTranslate",
    4662             :                          "Using FID=%s and disable -preserve_fid because not "
    4663             :                          "compatible with -explodecollection",
    4664           1 :                          poSrcLayer->GetFIDColumn());
    4665           1 :                 bPreserveFID = false;
    4666             :             }
    4667             :         }
    4668             :         // Detect scenario of converting from GPX to a format like GPKG
    4669             :         // Cf https://github.com/OSGeo/gdal/issues/9225
    4670         764 :         else if (!bPreserveFID && !m_bUnsetFid && !bAppend &&
    4671         761 :                  m_poSrcDS->GetDriver() &&
    4672         761 :                  EQUAL(m_poSrcDS->GetDriver()->GetDescription(), "GPX") &&
    4673           5 :                  pszDestCreationOptions &&
    4674           5 :                  (strstr(pszDestCreationOptions, "='FID'") != nullptr ||
    4675        1533 :                   strstr(pszDestCreationOptions, "=\"FID\"") != nullptr) &&
    4676           5 :                  CSLFetchNameValue(m_papszLCO, "FID") == nullptr)
    4677             :         {
    4678           5 :             CPLDebug("GDALVectorTranslate",
    4679             :                      "Forcing -preserve_fid because source is GPX and layers "
    4680             :                      "have FID cross references");
    4681           5 :             bPreserveFID = true;
    4682             :         }
    4683             :         // Detect scenario of converting GML2 with fid attribute to GPKG
    4684         807 :         else if (EQUAL(m_poDstDS->GetDriver()->GetDescription(), "GPKG") &&
    4685          43 :                  CSLFetchNameValue(m_papszLCO, "FID") == nullptr)
    4686             :         {
    4687          43 :             int nFieldIdx = poSrcLayer->GetLayerDefn()->GetFieldIndex("fid");
    4688          44 :             if (nFieldIdx >= 0 && poSrcLayer->GetLayerDefn()
    4689           1 :                                           ->GetFieldDefn(nFieldIdx)
    4690           1 :                                           ->GetType() == OFTString)
    4691             :             {
    4692           1 :                 CPLDebug("GDALVectorTranslate",
    4693             :                          "Source layer has a non-string 'fid' column. Using "
    4694             :                          "FID=gpkg_fid for GeoPackage");
    4695           1 :                 papszLCOTemp = CSLSetNameValue(papszLCOTemp, "FID", "gpkg_fid");
    4696             :             }
    4697             :         }
    4698             : 
    4699             :         // If bAddOverwriteLCO is ON (set up when overwriting a CARTO layer),
    4700             :         // set OVERWRITE to YES so the new layer overwrites the old one
    4701         804 :         if (bAddOverwriteLCO)
    4702             :         {
    4703           0 :             papszLCOTemp = CSLSetNameValue(papszLCOTemp, "OVERWRITE", "ON");
    4704           0 :             CPLDebug("GDALVectorTranslate", "Using OVERWRITE=ON");
    4705             :         }
    4706             : 
    4707        2411 :         if (m_bNativeData &&
    4708         803 :             poSrcLayer->GetMetadataItem("NATIVE_DATA", "NATIVE_DATA") !=
    4709          25 :                 nullptr &&
    4710          25 :             poSrcLayer->GetMetadataItem("NATIVE_MEDIA_TYPE", "NATIVE_DATA") !=
    4711          25 :                 nullptr &&
    4712          25 :             pszDestCreationOptions != nullptr &&
    4713        1632 :             strstr(pszDestCreationOptions, "NATIVE_DATA") != nullptr &&
    4714          25 :             strstr(pszDestCreationOptions, "NATIVE_MEDIA_TYPE") != nullptr)
    4715             :         {
    4716          25 :             papszLCOTemp = CSLSetNameValue(
    4717             :                 papszLCOTemp, "NATIVE_DATA",
    4718          25 :                 poSrcLayer->GetMetadataItem("NATIVE_DATA", "NATIVE_DATA"));
    4719             :             papszLCOTemp =
    4720          25 :                 CSLSetNameValue(papszLCOTemp, "NATIVE_MEDIA_TYPE",
    4721             :                                 poSrcLayer->GetMetadataItem("NATIVE_MEDIA_TYPE",
    4722          25 :                                                             "NATIVE_DATA"));
    4723          25 :             CPLDebug("GDALVectorTranslate", "Transferring layer NATIVE_DATA");
    4724             :         }
    4725             : 
    4726             :         // For FileGeodatabase, automatically set
    4727             :         // CREATE_SHAPE_AREA_AND_LENGTH_FIELDS=YES creation option if the source
    4728             :         // layer has a Shape_Area/Shape_Length field
    4729        1587 :         if (pszDestCreationOptions &&
    4730         783 :             strstr(pszDestCreationOptions,
    4731        1587 :                    "CREATE_SHAPE_AREA_AND_LENGTH_FIELDS") != nullptr &&
    4732          14 :             CSLFetchNameValue(m_papszLCO,
    4733             :                               "CREATE_SHAPE_AREA_AND_LENGTH_FIELDS") == nullptr)
    4734             :         {
    4735          14 :             const auto poSrcLayerDefn = poSrcLayer->GetLayerDefn();
    4736             :             const int nIdxShapeArea =
    4737          14 :                 poSrcLayerDefn->GetFieldIndex("Shape_Area");
    4738             :             const int nIdxShapeLength =
    4739          14 :                 poSrcLayerDefn->GetFieldIndex("Shape_Length");
    4740          16 :             if ((nIdxShapeArea >= 0 &&
    4741           2 :                  poSrcLayerDefn->GetFieldDefn(nIdxShapeArea)->GetDefault() !=
    4742           2 :                      nullptr &&
    4743           2 :                  EQUAL(
    4744             :                      poSrcLayerDefn->GetFieldDefn(nIdxShapeArea)->GetDefault(),
    4745           2 :                      "FILEGEODATABASE_SHAPE_AREA") &&
    4746           2 :                  (m_papszSelFields == nullptr ||
    4747          18 :                   CSLFindString(m_papszSelFields, "Shape_Area") >= 0)) ||
    4748           2 :                 (nIdxShapeLength >= 0 &&
    4749           2 :                  poSrcLayerDefn->GetFieldDefn(nIdxShapeLength)->GetDefault() !=
    4750           2 :                      nullptr &&
    4751           2 :                  EQUAL(poSrcLayerDefn->GetFieldDefn(nIdxShapeLength)
    4752             :                            ->GetDefault(),
    4753           2 :                        "FILEGEODATABASE_SHAPE_LENGTH") &&
    4754           2 :                  (m_papszSelFields == nullptr ||
    4755           0 :                   CSLFindString(m_papszSelFields, "Shape_Length") >= 0)))
    4756             :             {
    4757           4 :                 papszLCOTemp = CSLSetNameValue(
    4758             :                     papszLCOTemp, "CREATE_SHAPE_AREA_AND_LENGTH_FIELDS", "YES");
    4759           4 :                 CPLDebug("GDALVectorTranslate",
    4760             :                          "Setting CREATE_SHAPE_AREA_AND_LENGTH_FIELDS=YES");
    4761             :             }
    4762             :         }
    4763             : 
    4764             :         OGRGeomFieldDefn oGeomFieldDefn(
    4765             :             osGeomFieldName.c_str(),
    4766         804 :             static_cast<OGRwkbGeometryType>(eGCreateLayerType));
    4767         804 :         oGeomFieldDefn.SetSpatialRef(poOutputSRS);
    4768         804 :         oGeomFieldDefn.SetCoordinatePrecision(oCoordPrec);
    4769         804 :         oGeomFieldDefn.SetNullable(bGeomFieldNullable);
    4770         804 :         poDstLayer = m_poDstDS->CreateLayer(
    4771             :             pszNewLayerName,
    4772             :             eGCreateLayerType == wkbNone ? nullptr : &oGeomFieldDefn,
    4773             :             papszLCOTemp);
    4774         804 :         CSLDestroy(papszLCOTemp);
    4775             : 
    4776         804 :         if (poDstLayer == nullptr)
    4777             :         {
    4778           2 :             return nullptr;
    4779             :         }
    4780             : 
    4781             :         // Cf https://github.com/OSGeo/gdal/issues/6859
    4782             :         // warn if the user requests -t_srs but the driver uses a different SRS.
    4783         836 :         if (m_poOutputSRS != nullptr && m_bTransform && !psOptions->bQuiet &&
    4784             :             // MapInfo is somewhat lossy regarding SRS, so do not warn
    4785          34 :             !EQUAL(m_poDstDS->GetDriver()->GetDescription(), "MapInfo File"))
    4786             :         {
    4787          34 :             auto poCreatedSRS = poDstLayer->GetSpatialRef();
    4788          34 :             if (poCreatedSRS != nullptr)
    4789             :             {
    4790          20 :                 const char *const apszOptions[] = {
    4791             :                     "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES",
    4792             :                     "CRITERION=EQUIVALENT", nullptr};
    4793          20 :                 if (!poCreatedSRS->IsSame(m_poOutputSRS, apszOptions))
    4794             :                 {
    4795           1 :                     const char *pszTargetSRSName = m_poOutputSRS->GetName();
    4796           1 :                     const char *pszCreatedSRSName = poCreatedSRS->GetName();
    4797           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
    4798             :                              "Target SRS %s not taken into account as target "
    4799             :                              "driver likely implements on-the-fly reprojection "
    4800             :                              "to %s",
    4801             :                              pszTargetSRSName ? pszTargetSRSName : "",
    4802             :                              pszCreatedSRSName ? pszCreatedSRSName : "");
    4803             :                 }
    4804             :             }
    4805             :         }
    4806             : 
    4807         802 :         if (m_bCopyMD)
    4808             :         {
    4809        1596 :             const CPLStringList aosDomains(poSrcLayer->GetMetadataDomainList());
    4810        1150 :             for (const char *pszMD : aosDomains)
    4811             :             {
    4812         352 :                 if (!EQUAL(pszMD, "IMAGE_STRUCTURE") &&
    4813         352 :                     !EQUAL(pszMD, "SUBDATASETS"))
    4814             :                 {
    4815         352 :                     if (char **papszMD = poSrcLayer->GetMetadata(pszMD))
    4816         338 :                         poDstLayer->SetMetadata(papszMD, pszMD);
    4817             :                 }
    4818             :             }
    4819             :         }
    4820             : 
    4821         813 :         if (anRequestedGeomFields.empty() && nSrcGeomFieldCount > 1 &&
    4822          11 :             m_poDstDS->TestCapability(ODsCCreateGeomFieldAfterCreateLayer))
    4823             :         {
    4824         135 :             for (int i = 0; i < nSrcGeomFieldCount; i++)
    4825             :             {
    4826         125 :                 anRequestedGeomFields.push_back(i);
    4827             :             }
    4828             :         }
    4829             : 
    4830        1629 :         if (anRequestedGeomFields.size() > 1 ||
    4831         791 :             (anRequestedGeomFields.size() == 1 &&
    4832          36 :              m_poDstDS->TestCapability(ODsCCreateGeomFieldAfterCreateLayer)))
    4833             :         {
    4834         208 :             for (int i = 0; i < static_cast<int>(anRequestedGeomFields.size());
    4835             :                  i++)
    4836             :             {
    4837         162 :                 const int iSrcGeomField = anRequestedGeomFields[i];
    4838             :                 OGRGeomFieldDefn oGFldDefn(
    4839         324 :                     poSrcFDefn->GetGeomFieldDefn(iSrcGeomField));
    4840         162 :                 if (m_poOutputSRS != nullptr)
    4841             :                 {
    4842          13 :                     auto poOutputSRSClone = m_poOutputSRS->Clone();
    4843          13 :                     oGFldDefn.SetSpatialRef(poOutputSRSClone);
    4844          13 :                     poOutputSRSClone->Release();
    4845             :                 }
    4846         162 :                 if (bForceGType)
    4847             :                 {
    4848           1 :                     oGFldDefn.SetType(static_cast<OGRwkbGeometryType>(eGType));
    4849             :                 }
    4850             :                 else
    4851             :                 {
    4852         161 :                     eGType = oGFldDefn.GetType();
    4853         161 :                     eGType =
    4854         161 :                         ConvertType(m_eGeomTypeConversion,
    4855             :                                     static_cast<OGRwkbGeometryType>(eGType));
    4856         161 :                     eGType = ForceCoordDimension(eGType, m_nCoordDim);
    4857         161 :                     oGFldDefn.SetType(static_cast<OGRwkbGeometryType>(eGType));
    4858             :                 }
    4859         162 :                 if (m_bForceNullable)
    4860           2 :                     oGFldDefn.SetNullable(TRUE);
    4861         162 :                 poDstLayer->CreateGeomField(&oGFldDefn);
    4862             :             }
    4863             :         }
    4864             : 
    4865         802 :         bAppend = false;
    4866             :     }
    4867             : 
    4868             :     /* -------------------------------------------------------------------- */
    4869             :     /*      Otherwise we will append to it, if append was requested.        */
    4870             :     /* -------------------------------------------------------------------- */
    4871          41 :     else if (!bAppend && !m_bNewDataSource)
    4872             :     {
    4873           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4874             :                  "Layer %s already exists, and -append not specified.\n"
    4875             :                  "        Consider using -append, or -overwrite.",
    4876             :                  pszNewLayerName);
    4877           0 :         return nullptr;
    4878             :     }
    4879             :     else
    4880             :     {
    4881          41 :         if (CSLCount(m_papszLCO) > 0)
    4882             :         {
    4883           0 :             CPLError(
    4884             :                 CE_Warning, CPLE_AppDefined,
    4885             :                 "Layer creation options ignored since an existing layer is\n"
    4886             :                 "         being appended to.");
    4887             :         }
    4888             :     }
    4889             : 
    4890             :     /* -------------------------------------------------------------------- */
    4891             :     /*      Process Layer style table                                       */
    4892             :     /* -------------------------------------------------------------------- */
    4893             : 
    4894         843 :     poDstLayer->SetStyleTable(poSrcLayer->GetStyleTable());
    4895             :     /* -------------------------------------------------------------------- */
    4896             :     /*      Add fields.  Default to copy all field.                         */
    4897             :     /*      If only a subset of all fields requested, then output only      */
    4898             :     /*      the selected fields, and in the order that they were            */
    4899             :     /*      selected.                                                       */
    4900             :     /* -------------------------------------------------------------------- */
    4901         843 :     const int nSrcFieldCount = poSrcFDefn->GetFieldCount();
    4902         843 :     int iSrcFIDField = -1;
    4903             : 
    4904             :     // Initialize the index-to-index map to -1's
    4905        1686 :     std::vector<int> anMap(nSrcFieldCount, -1);
    4906             : 
    4907        1686 :     std::map<int, TargetLayerInfo::ResolvedInfo> oMapResolved;
    4908             : 
    4909             :     /* Determine if NUMERIC field width narrowing is allowed */
    4910         843 :     auto poSrcDriver = m_poSrcDS->GetDriver();
    4911             :     const char *pszSrcWidthIncludesDecimalSeparator{
    4912        1686 :         poSrcDriver ? poSrcDriver->GetMetadataItem(
    4913         843 :                           "DMD_NUMERIC_FIELD_WIDTH_INCLUDES_DECIMAL_SEPARATOR")
    4914         843 :                     : nullptr};
    4915         843 :     const bool bSrcWidthIncludesDecimalSeparator{
    4916        1071 :         pszSrcWidthIncludesDecimalSeparator &&
    4917         228 :         EQUAL(pszSrcWidthIncludesDecimalSeparator, "YES")};
    4918             :     const char *pszDstWidthIncludesDecimalSeparator{
    4919         843 :         m_poDstDS->GetDriver()->GetMetadataItem(
    4920         843 :             "DMD_NUMERIC_FIELD_WIDTH_INCLUDES_DECIMAL_SEPARATOR")};
    4921         843 :     const bool bDstWidthIncludesDecimalSeparator{
    4922         988 :         pszDstWidthIncludesDecimalSeparator &&
    4923         145 :         EQUAL(pszDstWidthIncludesDecimalSeparator, "YES")};
    4924             :     const char *pszSrcWidthIncludesMinusSign{
    4925        1686 :         poSrcDriver ? poSrcDriver->GetMetadataItem(
    4926         843 :                           "DMD_NUMERIC_FIELD_WIDTH_INCLUDES_SIGN")
    4927         843 :                     : nullptr};
    4928         843 :     const bool bSrcWidthIncludesMinusSign{
    4929        1071 :         pszSrcWidthIncludesMinusSign &&
    4930         228 :         EQUAL(pszSrcWidthIncludesMinusSign, "YES")};
    4931             :     const char *pszDstWidthIncludesMinusSign{
    4932         843 :         m_poDstDS->GetDriver()->GetMetadataItem(
    4933         843 :             "DMD_NUMERIC_FIELD_WIDTH_INCLUDES_SIGN")};
    4934         843 :     const bool bDstWidthIncludesMinusSign{
    4935         988 :         pszDstWidthIncludesMinusSign &&
    4936         145 :         EQUAL(pszDstWidthIncludesMinusSign, "YES")};
    4937             : 
    4938             :     // Calculate width delta
    4939         843 :     int iChangeWidthBy{0};
    4940             : 
    4941         843 :     if (bSrcWidthIncludesDecimalSeparator && !bDstWidthIncludesDecimalSeparator)
    4942             :     {
    4943         132 :         iChangeWidthBy--;
    4944             :     }
    4945         711 :     else if (!bSrcWidthIncludesDecimalSeparator &&
    4946             :              bDstWidthIncludesDecimalSeparator)
    4947             :     {
    4948          49 :         iChangeWidthBy++;
    4949             :     }
    4950             : 
    4951             :     // We cannot assume there is no minus sign, we can only inflate here
    4952         843 :     if (!bSrcWidthIncludesMinusSign && bDstWidthIncludesMinusSign)
    4953             :     {
    4954          49 :         iChangeWidthBy++;
    4955             :     }
    4956             : 
    4957         843 :     bool bError = false;
    4958        1686 :     OGRArrowArrayStream streamSrc;
    4959             :     const bool bUseWriteArrowBatch =
    4960         843 :         CanUseWriteArrowBatch(poSrcLayer, poDstLayer, bJustCreatedLayer,
    4961             :                               psOptions, bPreserveFID, bError, streamSrc);
    4962         843 :     if (bError)
    4963           0 :         return nullptr;
    4964             : 
    4965             :     /* Caution : at the time of writing, the MapInfo driver */
    4966             :     /* returns NULL until a field has been added */
    4967         843 :     OGRFeatureDefn *poDstFDefn = poDstLayer->GetLayerDefn();
    4968             : 
    4969         843 :     if (bUseWriteArrowBatch)
    4970             :     {
    4971             :         // Fields created above
    4972             :     }
    4973         701 :     else if (m_papszFieldMap && bAppend)
    4974             :     {
    4975           2 :         bool bIdentity = false;
    4976             : 
    4977           2 :         if (EQUAL(m_papszFieldMap[0], "identity"))
    4978           1 :             bIdentity = true;
    4979           1 :         else if (CSLCount(m_papszFieldMap) != nSrcFieldCount)
    4980             :         {
    4981           0 :             CPLError(
    4982             :                 CE_Failure, CPLE_AppDefined,
    4983             :                 "Field map should contain the value 'identity' or "
    4984             :                 "the same number of integer values as the source field count.");
    4985           0 :             return nullptr;
    4986             :         }
    4987             : 
    4988          32 :         for (int iField = 0; iField < nSrcFieldCount; iField++)
    4989             :         {
    4990          30 :             anMap[iField] = bIdentity ? iField : atoi(m_papszFieldMap[iField]);
    4991          30 :             if (anMap[iField] >= poDstFDefn->GetFieldCount())
    4992             :             {
    4993           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    4994           0 :                          "Invalid destination field index %d.", anMap[iField]);
    4995           0 :                 return nullptr;
    4996             :             }
    4997           2 :         }
    4998             :     }
    4999         699 :     else if (m_bSelFieldsSet && !bAppend)
    5000             :     {
    5001          15 :         int nDstFieldCount = poDstFDefn ? poDstFDefn->GetFieldCount() : 0;
    5002          40 :         for (int iField = 0; m_papszSelFields && m_papszSelFields[iField];
    5003             :              iField++)
    5004             :         {
    5005             :             const int iSrcField =
    5006          25 :                 poSrcFDefn->GetFieldIndex(m_papszSelFields[iField]);
    5007          25 :             if (iSrcField >= 0)
    5008             :             {
    5009             :                 OGRFieldDefn *poSrcFieldDefn =
    5010          22 :                     poSrcFDefn->GetFieldDefn(iSrcField);
    5011          44 :                 OGRFieldDefn oFieldDefn(poSrcFieldDefn);
    5012             : 
    5013          22 :                 DoFieldTypeConversion(
    5014             :                     m_poDstDS, oFieldDefn, m_papszFieldTypesToString,
    5015          22 :                     m_papszMapFieldType, m_bUnsetFieldWidth, psOptions->bQuiet,
    5016          22 :                     m_bForceNullable, m_bUnsetDefault);
    5017             : 
    5018          22 :                 if (iChangeWidthBy != 0 && oFieldDefn.GetType() == OFTReal &&
    5019           0 :                     oFieldDefn.GetWidth() != 0)
    5020             :                 {
    5021           0 :                     oFieldDefn.SetWidth(oFieldDefn.GetWidth() + iChangeWidthBy);
    5022             :                 }
    5023             : 
    5024             :                 /* The field may have been already created at layer creation */
    5025             :                 const int iDstField =
    5026             :                     poDstFDefn
    5027          22 :                         ? poDstFDefn->GetFieldIndex(oFieldDefn.GetNameRef())
    5028          22 :                         : -1;
    5029          22 :                 if (iDstField >= 0)
    5030             :                 {
    5031           0 :                     anMap[iSrcField] = iDstField;
    5032             :                 }
    5033          22 :                 else if (poDstLayer->CreateField(&oFieldDefn) == OGRERR_NONE)
    5034             :                 {
    5035             :                     /* now that we've created a field, GetLayerDefn() won't
    5036             :                      * return NULL */
    5037          22 :                     if (poDstFDefn == nullptr)
    5038           0 :                         poDstFDefn = poDstLayer->GetLayerDefn();
    5039             : 
    5040             :                     /* Sanity check : if it fails, the driver is buggy */
    5041          44 :                     if (poDstFDefn != nullptr &&
    5042          22 :                         poDstFDefn->GetFieldCount() != nDstFieldCount + 1)
    5043             :                     {
    5044           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    5045             :                                  "The output driver has claimed to have added "
    5046             :                                  "the %s field, but it did not!",
    5047             :                                  oFieldDefn.GetNameRef());
    5048             :                     }
    5049             :                     else
    5050             :                     {
    5051          22 :                         anMap[iSrcField] = nDstFieldCount;
    5052          22 :                         nDstFieldCount++;
    5053             :                     }
    5054             :                 }
    5055             :             }
    5056             :         }
    5057             : 
    5058             :         /* --------------------------------------------------------------------
    5059             :          */
    5060             :         /* Use SetIgnoredFields() on source layer if available */
    5061             :         /* --------------------------------------------------------------------
    5062             :          */
    5063          15 :         if (poSrcLayer->TestCapability(OLCIgnoreFields))
    5064             :         {
    5065          10 :             bool bUseIgnoredFields = true;
    5066          10 :             char **papszWHEREUsedFields = nullptr;
    5067             : 
    5068          10 :             if (m_pszWHERE)
    5069             :             {
    5070             :                 /* We must not ignore fields used in the -where expression
    5071             :                  * (#4015) */
    5072           4 :                 OGRFeatureQuery oFeatureQuery;
    5073           2 :                 if (oFeatureQuery.Compile(poSrcLayer->GetLayerDefn(),
    5074             :                                           m_pszWHERE, FALSE,
    5075           2 :                                           nullptr) == OGRERR_NONE)
    5076             :                 {
    5077           0 :                     papszWHEREUsedFields = oFeatureQuery.GetUsedFields();
    5078             :                 }
    5079             :                 else
    5080             :                 {
    5081           2 :                     bUseIgnoredFields = false;
    5082             :                 }
    5083             :             }
    5084             : 
    5085          10 :             char **papszIgnoredFields = nullptr;
    5086             : 
    5087          32 :             for (int iSrcField = 0;
    5088          32 :                  bUseIgnoredFields && iSrcField < poSrcFDefn->GetFieldCount();
    5089             :                  iSrcField++)
    5090             :             {
    5091             :                 const char *pszFieldName =
    5092          22 :                     poSrcFDefn->GetFieldDefn(iSrcField)->GetNameRef();
    5093          22 :                 bool bFieldRequested = false;
    5094          44 :                 for (int iField = 0;
    5095          44 :                      m_papszSelFields && m_papszSelFields[iField]; iField++)
    5096             :                 {
    5097          33 :                     if (EQUAL(pszFieldName, m_papszSelFields[iField]))
    5098             :                     {
    5099          11 :                         bFieldRequested = true;
    5100          11 :                         break;
    5101             :                     }
    5102             :                 }
    5103          22 :                 bFieldRequested |=
    5104          22 :                     CSLFindString(papszWHEREUsedFields, pszFieldName) >= 0;
    5105          22 :                 bFieldRequested |= (m_pszZField != nullptr &&
    5106           0 :                                     EQUAL(pszFieldName, m_pszZField));
    5107             : 
    5108             :                 /* If source field not requested, add it to ignored files list
    5109             :                  */
    5110          22 :                 if (!bFieldRequested)
    5111             :                     papszIgnoredFields =
    5112          11 :                         CSLAddString(papszIgnoredFields, pszFieldName);
    5113             :             }
    5114          10 :             if (bUseIgnoredFields)
    5115           8 :                 poSrcLayer->SetIgnoredFields(
    5116           8 :                     const_cast<const char **>(papszIgnoredFields));
    5117          10 :             CSLDestroy(papszIgnoredFields);
    5118          10 :             CSLDestroy(papszWHEREUsedFields);
    5119          15 :         }
    5120             :     }
    5121         684 :     else if (!bAppend || m_bAddMissingFields)
    5122             :     {
    5123         655 :         int nDstFieldCount = poDstFDefn ? poDstFDefn->GetFieldCount() : 0;
    5124             : 
    5125             :         const bool caseInsensitive =
    5126         655 :             !EQUAL(m_poDstDS->GetDriver()->GetDescription(), "GeoJSON");
    5127       12447 :         const auto formatName = [caseInsensitive](const char *name)
    5128             :         {
    5129       12447 :             if (caseInsensitive)
    5130             :             {
    5131       24230 :                 return CPLString(name).toupper();
    5132             :             }
    5133             :             else
    5134             :             {
    5135         332 :                 return CPLString(name);
    5136             :             }
    5137         655 :         };
    5138             : 
    5139             :         /* Save the map of existing fields, before creating new ones */
    5140             :         /* This helps when converting a source layer that has duplicated field
    5141             :          * names */
    5142             :         /* which is a bad idea */
    5143        1310 :         std::map<CPLString, int> oMapPreExistingFields;
    5144        1310 :         std::unordered_set<std::string> oSetDstFieldNames;
    5145         800 :         for (int iField = 0; iField < nDstFieldCount; iField++)
    5146             :         {
    5147             :             const char *pszFieldName =
    5148         145 :                 poDstFDefn->GetFieldDefn(iField)->GetNameRef();
    5149         290 :             CPLString osUpperFieldName(formatName(pszFieldName));
    5150         145 :             oSetDstFieldNames.insert(osUpperFieldName);
    5151         145 :             if (oMapPreExistingFields.find(osUpperFieldName) ==
    5152         290 :                 oMapPreExistingFields.end())
    5153         145 :                 oMapPreExistingFields[osUpperFieldName] = iField;
    5154             :             /*else
    5155             :                 CPLError(CE_Warning, CPLE_AppDefined,
    5156             :                          "The target layer has already a duplicated field name
    5157             :                '%s' before " "adding the fields of the source layer",
    5158             :                pszFieldName); */
    5159             :         }
    5160             : 
    5161         655 :         const char *pszFIDColumn = poDstLayer->GetFIDColumn();
    5162             : 
    5163        1310 :         std::vector<int> anSrcFieldIndices;
    5164         655 :         if (m_bSelFieldsSet)
    5165             :         {
    5166           2 :             for (int iField = 0; m_papszSelFields && m_papszSelFields[iField];
    5167             :                  iField++)
    5168             :             {
    5169             :                 const int iSrcField =
    5170           1 :                     poSrcFDefn->GetFieldIndex(m_papszSelFields[iField]);
    5171           1 :                 if (iSrcField >= 0)
    5172             :                 {
    5173           1 :                     anSrcFieldIndices.push_back(iSrcField);
    5174             :                 }
    5175             :             }
    5176             :         }
    5177             :         else
    5178             :         {
    5179        3791 :             for (int iField = 0; iField < nSrcFieldCount; iField++)
    5180             :             {
    5181        3137 :                 anSrcFieldIndices.push_back(iField);
    5182             :             }
    5183             :         }
    5184             : 
    5185        1310 :         std::unordered_set<std::string> oSetSrcFieldNames;
    5186        3794 :         for (int i = 0; i < poSrcFDefn->GetFieldCount(); i++)
    5187             :         {
    5188             :             oSetSrcFieldNames.insert(
    5189        3139 :                 formatName(poSrcFDefn->GetFieldDefn(i)->GetNameRef()));
    5190             :         }
    5191             : 
    5192             :         // For each source field name, memorize the last number suffix to have
    5193             :         // unique field names in the target. Let's imagine we have a source
    5194             :         // layer with the field name foo repeated twice After dealing the first
    5195             :         // field, oMapFieldNameToLastSuffix["foo"] will be 1, so when starting a
    5196             :         // unique name for the second field, we'll be able to start at 2. This
    5197             :         // avoids quadratic complexity if a big number of source field names are
    5198             :         // identical. Like in
    5199             :         // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37768
    5200        1310 :         std::map<std::string, int> oMapFieldNameToLastSuffix;
    5201             : 
    5202        3793 :         for (size_t i = 0; i < anSrcFieldIndices.size(); i++)
    5203             :         {
    5204        3138 :             const int iField = anSrcFieldIndices[i];
    5205             :             const OGRFieldDefn *poSrcFieldDefn =
    5206        3138 :                 poSrcFDefn->GetFieldDefn(iField);
    5207        3138 :             OGRFieldDefn oFieldDefn(poSrcFieldDefn);
    5208             : 
    5209             :             // Avoid creating a field with the same name as the FID column
    5210        6277 :             if (pszFIDColumn != nullptr &&
    5211        3139 :                 EQUAL(pszFIDColumn, oFieldDefn.GetNameRef()) &&
    5212           1 :                 (oFieldDefn.GetType() == OFTInteger ||
    5213           0 :                  oFieldDefn.GetType() == OFTInteger64))
    5214             :             {
    5215           1 :                 iSrcFIDField = iField;
    5216           1 :                 continue;
    5217             :             }
    5218             : 
    5219        3137 :             DoFieldTypeConversion(
    5220             :                 m_poDstDS, oFieldDefn, m_papszFieldTypesToString,
    5221        3137 :                 m_papszMapFieldType, m_bUnsetFieldWidth, psOptions->bQuiet,
    5222        3137 :                 m_bForceNullable, m_bUnsetDefault);
    5223             : 
    5224        3278 :             if (iChangeWidthBy != 0 && oFieldDefn.GetType() == OFTReal &&
    5225         141 :                 oFieldDefn.GetWidth() != 0)
    5226             :             {
    5227         101 :                 oFieldDefn.SetWidth(oFieldDefn.GetWidth() + iChangeWidthBy);
    5228             :             }
    5229             : 
    5230             :             /* The field may have been already created at layer creation */
    5231             :             {
    5232             :                 const auto oIter = oMapPreExistingFields.find(
    5233        3137 :                     formatName(oFieldDefn.GetNameRef()));
    5234        3137 :                 if (oIter != oMapPreExistingFields.end())
    5235             :                 {
    5236         115 :                     anMap[iField] = oIter->second;
    5237         115 :                     continue;
    5238             :                 }
    5239             :             }
    5240             : 
    5241        3022 :             bool bHasRenamed = false;
    5242             :             /* In case the field name already exists in the target layer, */
    5243             :             /* build a unique field name */
    5244        3022 :             if (oSetDstFieldNames.find(formatName(oFieldDefn.GetNameRef())) !=
    5245        6044 :                 oSetDstFieldNames.end())
    5246             :             {
    5247             :                 const CPLString osTmpNameRaddixUC(
    5248           4 :                     formatName(oFieldDefn.GetNameRef()));
    5249           2 :                 int nTry = 1;
    5250             :                 const auto oIter =
    5251           2 :                     oMapFieldNameToLastSuffix.find(osTmpNameRaddixUC);
    5252           2 :                 if (oIter != oMapFieldNameToLastSuffix.end())
    5253           1 :                     nTry = oIter->second;
    5254           2 :                 CPLString osTmpNameUC = osTmpNameRaddixUC;
    5255           2 :                 osTmpNameUC.reserve(osTmpNameUC.size() + 10);
    5256             :                 while (true)
    5257             :                 {
    5258           3 :                     ++nTry;
    5259             :                     char szTry[32];
    5260           3 :                     snprintf(szTry, sizeof(szTry), "%d", nTry);
    5261             :                     osTmpNameUC.replace(osTmpNameRaddixUC.size(),
    5262           3 :                                         std::string::npos, szTry);
    5263             : 
    5264             :                     /* Check that the proposed name doesn't exist either in the
    5265             :                      * already */
    5266             :                     /* created fields or in the source fields */
    5267           3 :                     if (oSetDstFieldNames.find(osTmpNameUC) ==
    5268           9 :                             oSetDstFieldNames.end() &&
    5269           3 :                         oSetSrcFieldNames.find(osTmpNameUC) ==
    5270           6 :                             oSetSrcFieldNames.end())
    5271             :                     {
    5272           2 :                         bHasRenamed = true;
    5273           2 :                         oFieldDefn.SetName(
    5274           4 :                             (CPLString(oFieldDefn.GetNameRef()) + szTry)
    5275             :                                 .c_str());
    5276           2 :                         oMapFieldNameToLastSuffix[osTmpNameRaddixUC] = nTry;
    5277           2 :                         break;
    5278             :                     }
    5279           1 :                 }
    5280             :             }
    5281             : 
    5282             :             // Create field domain in output dataset if not already existing.
    5283        6044 :             const std::string osDomainName(oFieldDefn.GetDomainName());
    5284        3022 :             if (!osDomainName.empty())
    5285             :             {
    5286          32 :                 if (m_poDstDS->TestCapability(ODsCAddFieldDomain) &&
    5287          16 :                     m_poDstDS->GetFieldDomain(osDomainName) == nullptr)
    5288             :                 {
    5289             :                     const auto poSrcDomain =
    5290          16 :                         m_poSrcDS->GetFieldDomain(osDomainName);
    5291          16 :                     if (poSrcDomain)
    5292             :                     {
    5293          28 :                         std::string failureReason;
    5294          14 :                         if (!m_poDstDS->AddFieldDomain(
    5295          28 :                                 std::unique_ptr<OGRFieldDomain>(
    5296          14 :                                     poSrcDomain->Clone()),
    5297          14 :                                 failureReason))
    5298             :                         {
    5299           0 :                             oFieldDefn.SetDomainName(std::string());
    5300           0 :                             CPLDebug("OGR2OGR", "Cannot create domain %s: %s",
    5301             :                                      osDomainName.c_str(),
    5302             :                                      failureReason.c_str());
    5303             :                         }
    5304             :                     }
    5305             :                     else
    5306             :                     {
    5307           2 :                         CPLDebug("OGR2OGR",
    5308             :                                  "Cannot find domain %s in source dataset",
    5309             :                                  osDomainName.c_str());
    5310             :                     }
    5311             :                 }
    5312          16 :                 if (m_poDstDS->GetFieldDomain(osDomainName) == nullptr)
    5313             :                 {
    5314           2 :                     oFieldDefn.SetDomainName(std::string());
    5315             :                 }
    5316             :             }
    5317             : 
    5318        3022 :             if (poDstLayer->CreateField(&oFieldDefn) == OGRERR_NONE)
    5319             :             {
    5320             :                 /* now that we've created a field, GetLayerDefn() won't return
    5321             :                  * NULL */
    5322        3002 :                 if (poDstFDefn == nullptr)
    5323           0 :                     poDstFDefn = poDstLayer->GetLayerDefn();
    5324             : 
    5325             :                 /* Sanity check : if it fails, the driver is buggy */
    5326        6004 :                 if (poDstFDefn != nullptr &&
    5327        3002 :                     poDstFDefn->GetFieldCount() != nDstFieldCount + 1)
    5328             :                 {
    5329           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    5330             :                              "The output driver has claimed to have added the "
    5331             :                              "%s field, but it did not!",
    5332             :                              oFieldDefn.GetNameRef());
    5333             :                 }
    5334             :                 else
    5335             :                 {
    5336        3002 :                     if (poDstFDefn != nullptr)
    5337             :                     {
    5338             :                         const char *pszNewFieldName =
    5339        3002 :                             poDstFDefn->GetFieldDefn(nDstFieldCount)
    5340        3002 :                                 ->GetNameRef();
    5341        3002 :                         if (bHasRenamed)
    5342             :                         {
    5343           2 :                             CPLError(CE_Warning, CPLE_AppDefined,
    5344             :                                      "Field '%s' already exists. Renaming it "
    5345             :                                      "as '%s'",
    5346             :                                      poSrcFieldDefn->GetNameRef(),
    5347             :                                      pszNewFieldName);
    5348             :                         }
    5349        3002 :                         oSetDstFieldNames.insert(formatName(pszNewFieldName));
    5350             :                     }
    5351             : 
    5352        3002 :                     anMap[iField] = nDstFieldCount;
    5353        3002 :                     nDstFieldCount++;
    5354             :                 }
    5355             :             }
    5356             : 
    5357        3022 :             if (m_bResolveDomains && !osDomainName.empty())
    5358             :             {
    5359             :                 const auto poSrcDomain =
    5360           3 :                     m_poSrcDS->GetFieldDomain(osDomainName);
    5361           3 :                 if (poSrcDomain && poSrcDomain->GetDomainType() == OFDT_CODED)
    5362             :                 {
    5363             :                     OGRFieldDefn oResolvedField(
    5364             :                         CPLSPrintf("%s_resolved", oFieldDefn.GetNameRef()),
    5365           2 :                         OFTString);
    5366           1 :                     if (poDstLayer->CreateField(&oResolvedField) == OGRERR_NONE)
    5367             :                     {
    5368             :                         TargetLayerInfo::ResolvedInfo resolvedInfo;
    5369           1 :                         resolvedInfo.nSrcField = iField;
    5370           1 :                         resolvedInfo.poDomain = poSrcDomain;
    5371           1 :                         oMapResolved[nDstFieldCount] = resolvedInfo;
    5372           1 :                         nDstFieldCount++;
    5373             :                     }
    5374             :                 }
    5375             :             }
    5376         655 :         }
    5377             :     }
    5378             :     else
    5379             :     {
    5380             :         /* For an existing layer, build the map by fetching the index in the
    5381             :          * destination */
    5382             :         /* layer for each source field */
    5383          29 :         if (poDstFDefn == nullptr)
    5384             :         {
    5385           0 :             CPLError(CE_Failure, CPLE_AppDefined, "poDstFDefn == NULL.");
    5386           0 :             return nullptr;
    5387             :         }
    5388             : 
    5389          95 :         for (int iField = 0; iField < nSrcFieldCount; iField++)
    5390             :         {
    5391          66 :             OGRFieldDefn *poSrcFieldDefn = poSrcFDefn->GetFieldDefn(iField);
    5392          66 :             const int iDstField = poDstLayer->FindFieldIndex(
    5393          66 :                 poSrcFieldDefn->GetNameRef(), m_bExactFieldNameMatch);
    5394          66 :             if (iDstField >= 0)
    5395          62 :                 anMap[iField] = iDstField;
    5396             :             else
    5397             :             {
    5398           4 :                 if (m_bExactFieldNameMatch)
    5399             :                 {
    5400           4 :                     const int iDstFieldCandidate = poDstLayer->FindFieldIndex(
    5401           4 :                         poSrcFieldDefn->GetNameRef(), false);
    5402           4 :                     if (iDstFieldCandidate >= 0)
    5403             :                     {
    5404           1 :                         CPLError(CE_Warning, CPLE_AppDefined,
    5405             :                                  "Source field '%s' could have been identified "
    5406             :                                  "with existing field '%s' of destination "
    5407             :                                  "layer '%s' if the -relaxedFieldNameMatch "
    5408             :                                  "option had been specified.",
    5409             :                                  poSrcFieldDefn->GetNameRef(),
    5410           1 :                                  poDstLayer->GetLayerDefn()
    5411           1 :                                      ->GetFieldDefn(iDstFieldCandidate)
    5412             :                                      ->GetNameRef(),
    5413           1 :                                  poDstLayer->GetName());
    5414             :                     }
    5415             :                 }
    5416             : 
    5417           4 :                 CPLDebug(
    5418             :                     "GDALVectorTranslate",
    5419             :                     "Skipping field '%s' not found in destination layer '%s'.",
    5420           4 :                     poSrcFieldDefn->GetNameRef(), poDstLayer->GetName());
    5421             :             }
    5422             :         }
    5423             :     }
    5424             : 
    5425          12 :     if (bOverwriteActuallyDone && !bAddOverwriteLCO &&
    5426          12 :         EQUAL(m_poDstDS->GetDriver()->GetDescription(), "PostgreSQL") &&
    5427         861 :         !psOptions->nLayerTransaction && psOptions->nGroupTransactions > 0 &&
    5428           6 :         CPLTestBool(CPLGetConfigOption("PG_COMMIT_WHEN_OVERWRITING", "YES")))
    5429             :     {
    5430           6 :         CPLDebug("GDALVectorTranslate",
    5431             :                  "Forcing transaction commit as table overwriting occurred");
    5432             :         // Commit when overwriting as this consumes a lot of PG resources
    5433             :         // and could result in """out of shared memory.
    5434             :         // You might need to increase max_locks_per_transaction."""" errors
    5435          12 :         if (m_poDstDS->CommitTransaction() == OGRERR_FAILURE ||
    5436           6 :             m_poDstDS->StartTransaction(psOptions->bForceTransaction) ==
    5437             :                 OGRERR_FAILURE)
    5438             :         {
    5439           0 :             return nullptr;
    5440             :         }
    5441           6 :         nTotalEventsDone = 0;
    5442             :     }
    5443             : 
    5444        1686 :     auto psInfo = std::make_unique<TargetLayerInfo>();
    5445         843 :     psInfo->m_bUseWriteArrowBatch = bUseWriteArrowBatch;
    5446         843 :     psInfo->m_nFeaturesRead = 0;
    5447         843 :     psInfo->m_bPerFeatureCT = false;
    5448         843 :     psInfo->m_poSrcLayer = poSrcLayer;
    5449         843 :     psInfo->m_poDstLayer = poDstLayer;
    5450         843 :     psInfo->m_aoReprojectionInfo.resize(
    5451         843 :         poDstLayer->GetLayerDefn()->GetGeomFieldCount());
    5452         843 :     psInfo->m_anMap = std::move(anMap);
    5453         843 :     psInfo->m_iSrcZField = iSrcZField;
    5454         843 :     psInfo->m_iSrcFIDField = iSrcFIDField;
    5455         843 :     if (anRequestedGeomFields.size() == 1)
    5456          36 :         psInfo->m_iRequestedSrcGeomField = anRequestedGeomFields[0];
    5457             :     else
    5458         807 :         psInfo->m_iRequestedSrcGeomField = -1;
    5459         843 :     psInfo->m_bPreserveFID = bPreserveFID;
    5460         843 :     psInfo->m_pszCTPipeline = m_pszCTPipeline;
    5461         843 :     psInfo->m_oMapResolved = std::move(oMapResolved);
    5462         844 :     for (const auto &kv : psInfo->m_oMapResolved)
    5463             :     {
    5464           1 :         const auto poDomain = kv.second.poDomain;
    5465             :         const auto poCodedDomain =
    5466           1 :             cpl::down_cast<const OGRCodedFieldDomain *>(poDomain);
    5467           1 :         const auto enumeration = poCodedDomain->GetEnumeration();
    5468           2 :         std::map<std::string, std::string> oMapCodeValue;
    5469           4 :         for (int i = 0; enumeration[i].pszCode != nullptr; ++i)
    5470             :         {
    5471           6 :             oMapCodeValue[enumeration[i].pszCode] =
    5472           6 :                 enumeration[i].pszValue ? enumeration[i].pszValue : "";
    5473             :         }
    5474           1 :         psInfo->m_oMapDomainToKV[poDomain] = std::move(oMapCodeValue);
    5475             :     }
    5476             : 
    5477             :     // Detect if we can directly pass the source feature to the CreateFeature()
    5478             :     // method of the target layer, without doing any copying of field content.
    5479         843 :     psInfo->m_bCanAvoidSetFrom = false;
    5480         843 :     if (!m_bExplodeCollections && iSrcZField == -1 && poDstFDefn != nullptr)
    5481             :     {
    5482         828 :         psInfo->m_bCanAvoidSetFrom = true;
    5483         828 :         const int nDstGeomFieldCount = poDstFDefn->GetGeomFieldCount();
    5484         828 :         if (nSrcFieldCount != poDstFDefn->GetFieldCount() ||
    5485             :             nSrcGeomFieldCount != nDstGeomFieldCount)
    5486             :         {
    5487         157 :             psInfo->m_bCanAvoidSetFrom = false;
    5488             :         }
    5489             :         else
    5490             :         {
    5491        3436 :             for (int i = 0; i < nSrcFieldCount; ++i)
    5492             :             {
    5493        2797 :                 auto poSrcFieldDefn = poSrcFDefn->GetFieldDefn(i);
    5494        2797 :                 auto poDstFieldDefn = poDstFDefn->GetFieldDefn(i);
    5495        5580 :                 if (poSrcFieldDefn->GetType() != poDstFieldDefn->GetType() ||
    5496        2783 :                     psInfo->m_anMap[i] != i)
    5497             :                 {
    5498          32 :                     psInfo->m_bCanAvoidSetFrom = false;
    5499          32 :                     break;
    5500             :                 }
    5501             :             }
    5502         671 :             if (!psInfo->m_bCanAvoidSetFrom && nSrcGeomFieldCount > 1)
    5503             :             {
    5504           4 :                 for (int i = 0; i < nSrcGeomFieldCount; ++i)
    5505             :                 {
    5506           3 :                     auto poSrcGeomFieldDefn = poSrcFDefn->GetGeomFieldDefn(i);
    5507           3 :                     auto poDstGeomFieldDefn = poDstFDefn->GetGeomFieldDefn(i);
    5508           3 :                     if (!EQUAL(poSrcGeomFieldDefn->GetNameRef(),
    5509             :                                poDstGeomFieldDefn->GetNameRef()))
    5510             :                     {
    5511           1 :                         psInfo->m_bCanAvoidSetFrom = false;
    5512           1 :                         break;
    5513             :                     }
    5514             :                 }
    5515             :             }
    5516             :         }
    5517             :     }
    5518             : 
    5519        1686 :     psInfo->m_pszSpatSRSDef = psOptions->osSpatSRSDef.empty()
    5520         843 :                                   ? nullptr
    5521           4 :                                   : psOptions->osSpatSRSDef.c_str();
    5522         843 :     psInfo->m_hSpatialFilter =
    5523         843 :         OGRGeometry::ToHandle(psOptions->poSpatialFilter.get());
    5524         843 :     psInfo->m_pszGeomField =
    5525         843 :         psOptions->bGeomFieldSet ? psOptions->osGeomField.c_str() : nullptr;
    5526             : 
    5527         843 :     if (psOptions->nTZOffsetInSec != TZ_OFFSET_INVALID && poDstFDefn)
    5528             :     {
    5529          15 :         for (int i = 0; i < poDstFDefn->GetFieldCount(); ++i)
    5530             :         {
    5531          10 :             if (poDstFDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
    5532             :             {
    5533           5 :                 psInfo->m_anDateTimeFieldIdx.push_back(i);
    5534             :             }
    5535             :         }
    5536             :     }
    5537             : 
    5538         843 :     psInfo->m_bSupportCurves =
    5539         843 :         CPL_TO_BOOL(poDstLayer->TestCapability(OLCCurveGeometries));
    5540             : 
    5541         843 :     psInfo->m_sArrowArrayStream = std::move(streamSrc);
    5542             : 
    5543         843 :     return psInfo;
    5544             : }
    5545             : 
    5546             : /************************************************************************/
    5547             : /*                               SetupCT()                              */
    5548             : /************************************************************************/
    5549             : 
    5550             : static bool
    5551         677 : SetupCT(TargetLayerInfo *psInfo, OGRLayer *poSrcLayer, bool bTransform,
    5552             :         bool bWrapDateline, const CPLString &osDateLineOffset,
    5553             :         const OGRSpatialReference *poUserSourceSRS, OGRFeature *poFeature,
    5554             :         const OGRSpatialReference *poOutputSRS,
    5555             :         OGRCoordinateTransformation *poGCPCoordTrans, bool bVerboseError)
    5556             : {
    5557         677 :     OGRLayer *poDstLayer = psInfo->m_poDstLayer;
    5558             :     const int nDstGeomFieldCount =
    5559         677 :         poDstLayer->GetLayerDefn()->GetGeomFieldCount();
    5560        1310 :     for (int iGeom = 0; iGeom < nDstGeomFieldCount; iGeom++)
    5561             :     {
    5562             :         /* --------------------------------------------------------------------
    5563             :          */
    5564             :         /*      Setup coordinate transformation if we need it. */
    5565             :         /* --------------------------------------------------------------------
    5566             :          */
    5567         634 :         const OGRSpatialReference *poSourceSRS = nullptr;
    5568         634 :         OGRCoordinateTransformation *poCT = nullptr;
    5569         634 :         char **papszTransformOptions = nullptr;
    5570             : 
    5571             :         int iSrcGeomField;
    5572             :         auto poDstGeomFieldDefn =
    5573         634 :             poDstLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
    5574         634 :         if (psInfo->m_iRequestedSrcGeomField >= 0)
    5575             :         {
    5576          34 :             iSrcGeomField = psInfo->m_iRequestedSrcGeomField;
    5577             :         }
    5578             :         else
    5579             :         {
    5580        1200 :             iSrcGeomField = poSrcLayer->GetLayerDefn()->GetGeomFieldIndex(
    5581         600 :                 poDstGeomFieldDefn->GetNameRef());
    5582         600 :             if (iSrcGeomField < 0)
    5583             :             {
    5584         238 :                 if (nDstGeomFieldCount == 1 &&
    5585         119 :                     poSrcLayer->GetLayerDefn()->GetGeomFieldCount() > 0)
    5586             :                 {
    5587         113 :                     iSrcGeomField = 0;
    5588             :                 }
    5589             :                 else
    5590             :                 {
    5591           6 :                     continue;
    5592             :                 }
    5593             :             }
    5594             :         }
    5595             : 
    5596         628 :         if (psInfo->m_nFeaturesRead == 0)
    5597             :         {
    5598         627 :             poSourceSRS = poUserSourceSRS;
    5599         627 :             if (poSourceSRS == nullptr)
    5600             :             {
    5601         619 :                 if (iSrcGeomField > 0)
    5602         118 :                     poSourceSRS = poSrcLayer->GetLayerDefn()
    5603         118 :                                       ->GetGeomFieldDefn(iSrcGeomField)
    5604         118 :                                       ->GetSpatialRef();
    5605             :                 else
    5606         501 :                     poSourceSRS = poSrcLayer->GetSpatialRef();
    5607             :             }
    5608             :         }
    5609         628 :         if (poSourceSRS == nullptr)
    5610             :         {
    5611         262 :             if (poFeature == nullptr)
    5612             :             {
    5613           1 :                 if (bVerboseError)
    5614             :                 {
    5615           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5616             :                              "Non-null feature expected to set transformation");
    5617             :                 }
    5618           1 :                 return false;
    5619             :             }
    5620             :             OGRGeometry *poSrcGeometry =
    5621         261 :                 poFeature->GetGeomFieldRef(iSrcGeomField);
    5622         261 :             if (poSrcGeometry)
    5623         217 :                 poSourceSRS = poSrcGeometry->getSpatialReference();
    5624         261 :             psInfo->m_bPerFeatureCT = (bTransform || bWrapDateline);
    5625             :         }
    5626             : 
    5627         627 :         if (bTransform)
    5628             :         {
    5629          36 :             if (poSourceSRS == nullptr && psInfo->m_pszCTPipeline == nullptr)
    5630             :             {
    5631           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5632             :                          "Can't transform coordinates, source layer has no\n"
    5633             :                          "coordinate system.  Use -s_srs to set one.");
    5634             : 
    5635           0 :                 return false;
    5636             :             }
    5637             : 
    5638          36 :             if (psInfo->m_pszCTPipeline == nullptr)
    5639             :             {
    5640          33 :                 CPLAssert(nullptr != poSourceSRS);
    5641          33 :                 CPLAssert(nullptr != poOutputSRS);
    5642             :             }
    5643             : 
    5644          36 :             if (psInfo->m_nFeaturesRead == 0 && !psInfo->m_bPerFeatureCT)
    5645             :             {
    5646             :                 const auto &supportedSRSList =
    5647          34 :                     poSrcLayer->GetSupportedSRSList(iGeom);
    5648          34 :                 if (!supportedSRSList.empty())
    5649             :                 {
    5650           1 :                     const char *const apszOptions[] = {
    5651             :                         "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
    5652           1 :                     for (const auto &poSRS : supportedSRSList)
    5653             :                     {
    5654           1 :                         if (poSRS->IsSame(poOutputSRS, apszOptions))
    5655             :                         {
    5656           2 :                             OGRSpatialReference oSourceSRSBackup;
    5657           1 :                             if (poSourceSRS)
    5658           1 :                                 oSourceSRSBackup = *poSourceSRS;
    5659           1 :                             if (poSrcLayer->SetActiveSRS(iGeom, poSRS.get()) ==
    5660             :                                 OGRERR_NONE)
    5661             :                             {
    5662           1 :                                 CPLDebug("ogr2ogr",
    5663             :                                          "Switching layer active SRS to %s",
    5664             :                                          poSRS->GetName());
    5665             : 
    5666           1 :                                 if (psInfo->m_hSpatialFilter != nullptr &&
    5667           0 :                                     ((psInfo->m_iRequestedSrcGeomField < 0 &&
    5668           0 :                                       iGeom == 0) ||
    5669             :                                      (iGeom ==
    5670           0 :                                       psInfo->m_iRequestedSrcGeomField)))
    5671             :                                 {
    5672           0 :                                     OGRSpatialReference oSpatSRS;
    5673           0 :                                     oSpatSRS.SetAxisMappingStrategy(
    5674             :                                         OAMS_TRADITIONAL_GIS_ORDER);
    5675           0 :                                     if (psInfo->m_pszSpatSRSDef)
    5676           0 :                                         oSpatSRS.SetFromUserInput(
    5677             :                                             psInfo->m_pszSpatSRSDef);
    5678           0 :                                     ApplySpatialFilter(
    5679             :                                         poSrcLayer,
    5680             :                                         OGRGeometry::FromHandle(
    5681             :                                             psInfo->m_hSpatialFilter),
    5682           0 :                                         !oSpatSRS.IsEmpty() ? &oSpatSRS
    5683           0 :                                         : !oSourceSRSBackup.IsEmpty()
    5684             :                                             ? &oSourceSRSBackup
    5685             :                                             : nullptr,
    5686             :                                         psInfo->m_pszGeomField, poOutputSRS);
    5687             :                                 }
    5688             : 
    5689           1 :                                 bTransform = false;
    5690             :                             }
    5691           1 :                             break;
    5692             :                         }
    5693             :                     }
    5694             :                 }
    5695             :             }
    5696             : 
    5697          36 :             if (!bTransform)
    5698             :             {
    5699             :                 // do nothing
    5700             :             }
    5701          36 :             else if (psInfo->m_aoReprojectionInfo[iGeom].m_poCT != nullptr &&
    5702           1 :                      psInfo->m_aoReprojectionInfo[iGeom]
    5703           1 :                              .m_poCT->GetSourceCS() == poSourceSRS)
    5704             :             {
    5705           0 :                 poCT = psInfo->m_aoReprojectionInfo[iGeom].m_poCT.get();
    5706             :             }
    5707             :             else
    5708             :             {
    5709          35 :                 OGRCoordinateTransformationOptions options;
    5710          35 :                 if (psInfo->m_pszCTPipeline)
    5711             :                 {
    5712           3 :                     options.SetCoordinateOperation(psInfo->m_pszCTPipeline,
    5713             :                                                    false);
    5714             :                 }
    5715          35 :                 poCT = OGRCreateCoordinateTransformation(poSourceSRS,
    5716             :                                                          poOutputSRS, options);
    5717          35 :                 if (poCT == nullptr)
    5718             :                 {
    5719           0 :                     char *pszWKT = nullptr;
    5720             : 
    5721           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5722             :                              "Failed to create coordinate transformation "
    5723             :                              "between the\n"
    5724             :                              "following coordinate systems.  This may be "
    5725             :                              "because they\n"
    5726             :                              "are not transformable.");
    5727             : 
    5728           0 :                     if (poSourceSRS)
    5729             :                     {
    5730           0 :                         poSourceSRS->exportToPrettyWkt(&pszWKT, FALSE);
    5731           0 :                         CPLError(CE_Failure, CPLE_AppDefined, "Source:\n%s",
    5732             :                                  pszWKT);
    5733           0 :                         CPLFree(pszWKT);
    5734             :                     }
    5735             : 
    5736           0 :                     if (poOutputSRS)
    5737             :                     {
    5738           0 :                         poOutputSRS->exportToPrettyWkt(&pszWKT, FALSE);
    5739           0 :                         CPLError(CE_Failure, CPLE_AppDefined, "Target:\n%s",
    5740             :                                  pszWKT);
    5741           0 :                         CPLFree(pszWKT);
    5742             :                     }
    5743             : 
    5744           0 :                     return false;
    5745             :                 }
    5746          35 :                 poCT = new CompositeCT(poGCPCoordTrans, false, poCT, true);
    5747          35 :                 psInfo->m_aoReprojectionInfo[iGeom].m_poCT.reset(poCT);
    5748          35 :                 psInfo->m_aoReprojectionInfo[iGeom].m_bCanInvalidateValidity =
    5749          69 :                     !(poGCPCoordTrans == nullptr && poSourceSRS &&
    5750          34 :                       poSourceSRS->IsGeographic() && poOutputSRS &&
    5751           3 :                       poOutputSRS->IsGeographic());
    5752             :             }
    5753             :         }
    5754             :         else
    5755             :         {
    5756         591 :             const char *const apszOptions[] = {
    5757             :                 "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES",
    5758             :                 "CRITERION=EQUIVALENT", nullptr};
    5759             :             auto poDstGeomFieldDefnSpatialRef =
    5760         591 :                 poDstGeomFieldDefn->GetSpatialRef();
    5761         332 :             if (poSourceSRS && poDstGeomFieldDefnSpatialRef &&
    5762         251 :                 poSourceSRS->GetDataAxisToSRSAxisMapping() !=
    5763             :                     poDstGeomFieldDefnSpatialRef
    5764         923 :                         ->GetDataAxisToSRSAxisMapping() &&
    5765           2 :                 poSourceSRS->IsSame(poDstGeomFieldDefnSpatialRef, apszOptions))
    5766             :             {
    5767           0 :                 psInfo->m_aoReprojectionInfo[iGeom].m_poCT.reset(
    5768             :                     new CompositeCT(
    5769           0 :                         new AxisMappingCoordinateTransformation(
    5770           0 :                             poSourceSRS->GetDataAxisToSRSAxisMapping(),
    5771             :                             poDstGeomFieldDefnSpatialRef
    5772           0 :                                 ->GetDataAxisToSRSAxisMapping()),
    5773           0 :                         true, poGCPCoordTrans, false));
    5774           0 :                 poCT = psInfo->m_aoReprojectionInfo[iGeom].m_poCT.get();
    5775             :             }
    5776         591 :             else if (poGCPCoordTrans)
    5777             :             {
    5778          10 :                 psInfo->m_aoReprojectionInfo[iGeom].m_poCT.reset(
    5779           5 :                     new CompositeCT(poGCPCoordTrans, false, nullptr, false));
    5780           5 :                 poCT = psInfo->m_aoReprojectionInfo[iGeom].m_poCT.get();
    5781             :             }
    5782             :         }
    5783             : 
    5784         627 :         if (bWrapDateline)
    5785             :         {
    5786           5 :             if (bTransform && poCT != nullptr && poOutputSRS != nullptr &&
    5787           1 :                 poOutputSRS->IsGeographic())
    5788             :             {
    5789             :                 papszTransformOptions =
    5790           1 :                     CSLAddString(papszTransformOptions, "WRAPDATELINE=YES");
    5791           1 :                 if (!osDateLineOffset.empty())
    5792             :                 {
    5793           1 :                     CPLString soOffset("DATELINEOFFSET=");
    5794           1 :                     soOffset += osDateLineOffset;
    5795             :                     papszTransformOptions =
    5796           1 :                         CSLAddString(papszTransformOptions, soOffset);
    5797             :                 }
    5798             :             }
    5799           3 :             else if (poSourceSRS != nullptr && poSourceSRS->IsGeographic())
    5800             :             {
    5801             :                 papszTransformOptions =
    5802           3 :                     CSLAddString(papszTransformOptions, "WRAPDATELINE=YES");
    5803           3 :                 if (!osDateLineOffset.empty())
    5804             :                 {
    5805           3 :                     CPLString soOffset("DATELINEOFFSET=");
    5806           3 :                     soOffset += osDateLineOffset;
    5807             :                     papszTransformOptions =
    5808           3 :                         CSLAddString(papszTransformOptions, soOffset);
    5809             :                 }
    5810             :             }
    5811             :             else
    5812             :             {
    5813             :                 static bool bHasWarned = false;
    5814           0 :                 if (!bHasWarned)
    5815           0 :                     CPLError(CE_Failure, CPLE_IllegalArg,
    5816             :                              "-wrapdateline option only works when "
    5817             :                              "reprojecting to a geographic SRS");
    5818           0 :                 bHasWarned = true;
    5819             :             }
    5820             : 
    5821           4 :             psInfo->m_aoReprojectionInfo[iGeom].m_aosTransformOptions.Assign(
    5822           4 :                 papszTransformOptions);
    5823             :         }
    5824             :     }
    5825         676 :     return true;
    5826             : }
    5827             : 
    5828             : /************************************************************************/
    5829             : /*                 LayerTranslator::TranslateArrow()                    */
    5830             : /************************************************************************/
    5831             : 
    5832         142 : bool LayerTranslator::TranslateArrow(
    5833             :     TargetLayerInfo *psInfo, GIntBig nCountLayerFeatures,
    5834             :     GIntBig *pnReadFeatureCount, GDALProgressFunc pfnProgress,
    5835             :     void *pProgressArg, const GDALVectorTranslateOptions *psOptions)
    5836             : {
    5837             :     struct ArrowSchema schema;
    5838         284 :     CPLStringList aosOptionsWriteArrowBatch;
    5839         142 :     if (psInfo->m_bPreserveFID)
    5840             :     {
    5841             :         aosOptionsWriteArrowBatch.SetNameValue(
    5842          16 :             "FID", psInfo->m_poSrcLayer->GetFIDColumn());
    5843             :         aosOptionsWriteArrowBatch.SetNameValue("IF_FID_NOT_PRESERVED",
    5844          16 :                                                "WARNING");
    5845             :     }
    5846             : 
    5847         142 :     if (psInfo->m_sArrowArrayStream.get_schema(&schema) != 0)
    5848             :     {
    5849           0 :         CPLError(CE_Failure, CPLE_AppDefined, "stream.get_schema() failed");
    5850           0 :         return false;
    5851             :     }
    5852             : 
    5853         142 :     int iArrowGeomFieldIndex = -1;
    5854         142 :     if (m_bTransform)
    5855             :     {
    5856          11 :         iArrowGeomFieldIndex = GetArrowGeomFieldIndex(
    5857          11 :             &schema, psInfo->m_poSrcLayer->GetGeometryColumn());
    5858          11 :         if (!SetupCT(psInfo, psInfo->m_poSrcLayer, m_bTransform,
    5859          11 :                      m_bWrapDateline, m_osDateLineOffset, m_poUserSourceSRS,
    5860             :                      nullptr, m_poOutputSRS, m_poGCPCoordTrans, false))
    5861             :         {
    5862           0 :             return false;
    5863             :         }
    5864             :     }
    5865             : 
    5866         142 :     bool bRet = true;
    5867             : 
    5868         142 :     GIntBig nCount = 0;
    5869         142 :     bool bGoOn = true;
    5870         142 :     std::vector<GByte> abyModifiedWKB;
    5871         142 :     const int nNumReprojectionThreads = []()
    5872             :     {
    5873         142 :         const int nNumCPUs = CPLGetNumCPUs();
    5874         142 :         if (nNumCPUs <= 1)
    5875             :         {
    5876           0 :             return 1;
    5877             :         }
    5878             :         else
    5879             :         {
    5880             :             const char *pszNumThreads =
    5881         142 :                 CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
    5882         142 :             if (pszNumThreads)
    5883             :             {
    5884           0 :                 if (EQUAL(pszNumThreads, "ALL_CPUS"))
    5885           0 :                     return CPLGetNumCPUs();
    5886           0 :                 return std::min(atoi(pszNumThreads), 1024);
    5887             :             }
    5888             :             else
    5889             :             {
    5890         142 :                 return std::max(2, nNumCPUs / 2);
    5891             :             }
    5892             :         }
    5893         142 :     }();
    5894             : 
    5895             :     // Somewhat arbitrary threshold (config option only/mostly for autotest purposes)
    5896         142 :     const int MIN_FEATURES_FOR_THREADED_REPROJ = atoi(CPLGetConfigOption(
    5897             :         "OGR2OGR_MIN_FEATURES_FOR_THREADED_REPROJ", "10000"));
    5898             : 
    5899         285 :     while (bGoOn)
    5900             :     {
    5901             :         struct ArrowArray array;
    5902             :         // Acquire source batch
    5903         283 :         if (psInfo->m_sArrowArrayStream.get_next(&array) != 0)
    5904             :         {
    5905           0 :             CPLError(CE_Failure, CPLE_AppDefined, "stream.get_next() failed");
    5906           0 :             bRet = false;
    5907         140 :             break;
    5908             :         }
    5909             : 
    5910         283 :         if (array.release == nullptr)
    5911             :         {
    5912             :             // End of stream
    5913         140 :             break;
    5914             :         }
    5915             : 
    5916             :         // Limit number of features in batch if needed
    5917         143 :         if (psOptions->nLimit >= 0 &&
    5918           2 :             nCount + array.length >= psOptions->nLimit)
    5919             :         {
    5920           2 :             const auto nAdjustedLength = psOptions->nLimit - nCount;
    5921          14 :             for (int i = 0; i < array.n_children; ++i)
    5922             :             {
    5923          12 :                 if (array.children[i]->length == array.length)
    5924          12 :                     array.children[i]->length = nAdjustedLength;
    5925             :             }
    5926           2 :             array.length = nAdjustedLength;
    5927           2 :             nCount = psOptions->nLimit;
    5928           2 :             bGoOn = false;
    5929             :         }
    5930             :         else
    5931             :         {
    5932         141 :             nCount += array.length;
    5933             :         }
    5934             : 
    5935         143 :         const auto nArrayLength = array.length;
    5936             : 
    5937             :         // Coordinate reprojection
    5938         143 :         if (m_bTransform)
    5939             :         {
    5940             :             struct GeomArrayReleaser
    5941             :             {
    5942             :                 const void *origin_buffers_2 = nullptr;
    5943             :                 void (*origin_release)(struct ArrowArray *) = nullptr;
    5944             :                 void *origin_private_data = nullptr;
    5945             : 
    5946          10 :                 static void init(struct ArrowArray *psGeomArray)
    5947             :                 {
    5948          10 :                     GeomArrayReleaser *releaser = new GeomArrayReleaser();
    5949          10 :                     CPLAssert(psGeomArray->n_buffers >= 3);
    5950          10 :                     releaser->origin_buffers_2 = psGeomArray->buffers[2];
    5951          10 :                     releaser->origin_private_data = psGeomArray->private_data;
    5952          10 :                     releaser->origin_release = psGeomArray->release;
    5953          10 :                     psGeomArray->release = GeomArrayReleaser::release;
    5954          10 :                     psGeomArray->private_data = releaser;
    5955          10 :                 }
    5956             : 
    5957          10 :                 static void release(struct ArrowArray *psGeomArray)
    5958             :                 {
    5959          10 :                     GeomArrayReleaser *releaser =
    5960             :                         static_cast<GeomArrayReleaser *>(
    5961             :                             psGeomArray->private_data);
    5962          10 :                     psGeomArray->buffers[2] = releaser->origin_buffers_2;
    5963          10 :                     psGeomArray->private_data = releaser->origin_private_data;
    5964          10 :                     psGeomArray->release = releaser->origin_release;
    5965          10 :                     if (psGeomArray->release)
    5966          10 :                         psGeomArray->release(psGeomArray);
    5967          10 :                     delete releaser;
    5968          10 :                 }
    5969             :             };
    5970             : 
    5971          10 :             auto *psGeomArray = array.children[iArrowGeomFieldIndex];
    5972          10 :             GeomArrayReleaser::init(psGeomArray);
    5973             : 
    5974          10 :             GByte *pabyWKB = static_cast<GByte *>(
    5975          10 :                 const_cast<void *>(psGeomArray->buffers[2]));
    5976          10 :             const uint32_t *panOffsets =
    5977          10 :                 static_cast<const uint32_t *>(psGeomArray->buffers[1]);
    5978          10 :             auto poCT = psInfo->m_aoReprojectionInfo[0].m_poCT.get();
    5979             : 
    5980             :             try
    5981             :             {
    5982          10 :                 abyModifiedWKB.resize(panOffsets[nArrayLength]);
    5983             :             }
    5984           0 :             catch (const std::exception &)
    5985             :             {
    5986           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
    5987           0 :                 bRet = false;
    5988           0 :                 if (array.release)
    5989           0 :                     array.release(&array);
    5990           0 :                 break;
    5991             :             }
    5992          10 :             memcpy(abyModifiedWKB.data(), pabyWKB, panOffsets[nArrayLength]);
    5993          10 :             psGeomArray->buffers[2] = abyModifiedWKB.data();
    5994             : 
    5995          10 :             std::atomic<bool> atomicRet{true};
    5996             :             const auto oReprojectionLambda =
    5997          15 :                 [psGeomArray, nArrayLength, panOffsets, &atomicRet,
    5998       40061 :                  &abyModifiedWKB, &poCT](int iThread, int nThreads)
    5999             :             {
    6000             :                 OGRWKBTransformCache oCache;
    6001          15 :                 OGREnvelope3D sEnv3D;
    6002             :                 auto poThisCT =
    6003          15 :                     std::unique_ptr<OGRCoordinateTransformation>(poCT->Clone());
    6004          15 :                 if (!poThisCT)
    6005             :                 {
    6006           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    6007             :                              "Cannot clone OGRCoordinateTransformation");
    6008           0 :                     atomicRet = false;
    6009           0 :                     return;
    6010             :                 }
    6011             : 
    6012          15 :                 const GByte *pabyValidity =
    6013          15 :                     static_cast<const GByte *>(psGeomArray->buffers[0]);
    6014          15 :                 const size_t iStart =
    6015          15 :                     static_cast<size_t>(iThread * nArrayLength / nThreads);
    6016          15 :                 const size_t iMax = static_cast<size_t>(
    6017          15 :                     (iThread + 1) * nArrayLength / nThreads);
    6018       10023 :                 for (size_t i = iStart; i < iMax; ++i)
    6019             :                 {
    6020       10009 :                     const size_t iShifted =
    6021       10009 :                         static_cast<size_t>(i + psGeomArray->offset);
    6022       10009 :                     if (!pabyValidity || (pabyValidity[iShifted >> 8] &
    6023          24 :                                           (1 << (iShifted % 8))) != 0)
    6024             :                     {
    6025       10002 :                         const auto nWKBSize =
    6026       10002 :                             panOffsets[iShifted + 1] - panOffsets[iShifted];
    6027       20003 :                         if (!OGRWKBTransform(
    6028       10007 :                                 abyModifiedWKB.data() + panOffsets[iShifted],
    6029             :                                 nWKBSize, poThisCT.get(), oCache, sEnv3D))
    6030             :                         {
    6031           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    6032             :                                      "Reprojection failed");
    6033           0 :                             atomicRet = false;
    6034           0 :                             break;
    6035             :                         }
    6036             :                     }
    6037             :                 }
    6038          10 :             };
    6039             : 
    6040          10 :             if (nArrayLength >= MIN_FEATURES_FOR_THREADED_REPROJ &&
    6041           5 :                 nNumReprojectionThreads >= 2)
    6042             :             {
    6043          10 :                 std::vector<std::future<void>> oTasks;
    6044          15 :                 for (int iThread = 0; iThread < nNumReprojectionThreads;
    6045             :                      ++iThread)
    6046             :                 {
    6047          20 :                     oTasks.emplace_back(std::async(std::launch::async,
    6048             :                                                    oReprojectionLambda, iThread,
    6049          10 :                                                    nNumReprojectionThreads));
    6050             :                 }
    6051          15 :                 for (auto &oTask : oTasks)
    6052             :                 {
    6053          10 :                     oTask.get();
    6054           5 :                 }
    6055             :             }
    6056             :             else
    6057             :             {
    6058           5 :                 oReprojectionLambda(0, 1);
    6059             :             }
    6060             : 
    6061          10 :             bRet = atomicRet;
    6062          10 :             if (!bRet)
    6063             :             {
    6064           0 :                 if (array.release)
    6065           0 :                     array.release(&array);
    6066           0 :                 break;
    6067             :             }
    6068             :         }
    6069             : 
    6070             :         // Write batch to target layer
    6071         143 :         const bool bWriteOK = psInfo->m_poDstLayer->WriteArrowBatch(
    6072         143 :             &schema, &array, aosOptionsWriteArrowBatch.List());
    6073             : 
    6074         143 :         if (array.release)
    6075          26 :             array.release(&array);
    6076             : 
    6077         143 :         if (!bWriteOK)
    6078             :         {
    6079           0 :             CPLError(CE_Failure, CPLE_AppDefined, "WriteArrowBatch() failed");
    6080           0 :             bRet = false;
    6081           0 :             break;
    6082             :         }
    6083             : 
    6084             :         /* Report progress */
    6085         143 :         if (pfnProgress)
    6086             :         {
    6087           0 :             if (!pfnProgress(nCountLayerFeatures
    6088           0 :                                  ? nCount * 1.0 / nCountLayerFeatures
    6089             :                                  : 1.0,
    6090             :                              "", pProgressArg))
    6091             :             {
    6092           0 :                 bGoOn = false;
    6093           0 :                 bRet = false;
    6094             :             }
    6095             :         }
    6096             : 
    6097         143 :         if (pnReadFeatureCount)
    6098           0 :             *pnReadFeatureCount = nCount;
    6099             :     }
    6100             : 
    6101         142 :     schema.release(&schema);
    6102             : 
    6103         142 :     return bRet;
    6104             : }
    6105             : 
    6106             : /************************************************************************/
    6107             : /*                     LayerTranslator::Translate()                     */
    6108             : /************************************************************************/
    6109             : 
    6110        1672 : bool LayerTranslator::Translate(
    6111             :     OGRFeature *poFeatureIn, TargetLayerInfo *psInfo,
    6112             :     GIntBig nCountLayerFeatures, GIntBig *pnReadFeatureCount,
    6113             :     GIntBig &nTotalEventsDone, GDALProgressFunc pfnProgress, void *pProgressArg,
    6114             :     const GDALVectorTranslateOptions *psOptions)
    6115             : {
    6116        1672 :     if (psInfo->m_bUseWriteArrowBatch)
    6117             :     {
    6118         142 :         return TranslateArrow(psInfo, nCountLayerFeatures, pnReadFeatureCount,
    6119         142 :                               pfnProgress, pProgressArg, psOptions);
    6120             :     }
    6121             : 
    6122        1530 :     const int eGType = m_eGType;
    6123        1530 :     const OGRSpatialReference *poOutputSRS = m_poOutputSRS;
    6124             : 
    6125        1530 :     OGRLayer *poSrcLayer = psInfo->m_poSrcLayer;
    6126        1530 :     OGRLayer *poDstLayer = psInfo->m_poDstLayer;
    6127        1530 :     const int *const panMap = psInfo->m_anMap.data();
    6128        1530 :     const int iSrcZField = psInfo->m_iSrcZField;
    6129        1530 :     const bool bPreserveFID = psInfo->m_bPreserveFID;
    6130        1530 :     const auto poSrcFDefn = poSrcLayer->GetLayerDefn();
    6131        1530 :     const auto poDstFDefn = poDstLayer->GetLayerDefn();
    6132        1530 :     const int nSrcGeomFieldCount = poSrcFDefn->GetGeomFieldCount();
    6133        1530 :     const int nDstGeomFieldCount = poDstFDefn->GetGeomFieldCount();
    6134        1530 :     const bool bExplodeCollections =
    6135        1530 :         m_bExplodeCollections && nDstGeomFieldCount <= 1;
    6136        1530 :     const int iRequestedSrcGeomField = psInfo->m_iRequestedSrcGeomField;
    6137             : 
    6138        1530 :     if (poOutputSRS == nullptr && !m_bNullifyOutputSRS)
    6139             :     {
    6140        1499 :         if (nSrcGeomFieldCount == 1)
    6141             :         {
    6142         499 :             poOutputSRS = poSrcLayer->GetSpatialRef();
    6143             :         }
    6144        1000 :         else if (iRequestedSrcGeomField > 0)
    6145             :         {
    6146           1 :             poOutputSRS = poSrcLayer->GetLayerDefn()
    6147           1 :                               ->GetGeomFieldDefn(iRequestedSrcGeomField)
    6148           1 :                               ->GetSpatialRef();
    6149             :         }
    6150             :     }
    6151             : 
    6152             :     /* -------------------------------------------------------------------- */
    6153             :     /*      Transfer features.                                              */
    6154             :     /* -------------------------------------------------------------------- */
    6155        1530 :     if (psOptions->nGroupTransactions)
    6156             :     {
    6157        1529 :         if (psOptions->nLayerTransaction)
    6158             :         {
    6159         463 :             if (poDstLayer->StartTransaction() == OGRERR_FAILURE)
    6160             :             {
    6161           0 :                 delete poFeatureIn;
    6162           0 :                 return false;
    6163             :             }
    6164             :         }
    6165             :     }
    6166             : 
    6167        1530 :     std::unique_ptr<OGRFeature> poFeature;
    6168        3060 :     std::unique_ptr<OGRFeature> poDstFeature(new OGRFeature(poDstFDefn));
    6169        1530 :     int nFeaturesInTransaction = 0;
    6170        1530 :     GIntBig nCount = 0; /* written + failed */
    6171        1530 :     GIntBig nFeaturesWritten = 0;
    6172        1530 :     bool bRunSetPrecisionEvaluated = false;
    6173        1530 :     bool bRunSetPrecision = false;
    6174             : 
    6175        1530 :     bool bRet = true;
    6176        1530 :     CPLErrorReset();
    6177             : 
    6178        1530 :     bool bSetupCTOK = false;
    6179        1530 :     if (m_bTransform && psInfo->m_nFeaturesRead == 0 &&
    6180          24 :         !psInfo->m_bPerFeatureCT)
    6181             :     {
    6182          24 :         bSetupCTOK = SetupCT(psInfo, poSrcLayer, m_bTransform, m_bWrapDateline,
    6183          24 :                              m_osDateLineOffset, m_poUserSourceSRS, nullptr,
    6184             :                              poOutputSRS, m_poGCPCoordTrans, false);
    6185             :     }
    6186             : 
    6187             :     while (true)
    6188             :     {
    6189        5196 :         if (m_nLimit >= 0 && psInfo->m_nFeaturesRead >= m_nLimit)
    6190             :         {
    6191           9 :             break;
    6192             :         }
    6193             : 
    6194        5187 :         if (poFeatureIn != nullptr)
    6195         974 :             poFeature.reset(poFeatureIn);
    6196        4213 :         else if (psOptions->nFIDToFetch != OGRNullFID)
    6197           5 :             poFeature.reset(poSrcLayer->GetFeature(psOptions->nFIDToFetch));
    6198             :         else
    6199        4208 :             poFeature.reset(poSrcLayer->GetNextFeature());
    6200             : 
    6201        5187 :         if (poFeature == nullptr)
    6202             :         {
    6203         537 :             if (CPLGetLastErrorType() == CE_Failure)
    6204             :             {
    6205           1 :                 bRet = false;
    6206             :             }
    6207         537 :             break;
    6208             :         }
    6209             : 
    6210        4650 :         if (!bSetupCTOK &&
    6211        4550 :             (psInfo->m_nFeaturesRead == 0 || psInfo->m_bPerFeatureCT))
    6212             :         {
    6213        1284 :             if (!SetupCT(psInfo, poSrcLayer, m_bTransform, m_bWrapDateline,
    6214         642 :                          m_osDateLineOffset, m_poUserSourceSRS, poFeature.get(),
    6215             :                          poOutputSRS, m_poGCPCoordTrans, true))
    6216             :             {
    6217           4 :                 return false;
    6218             :             }
    6219             :         }
    6220             : 
    6221        4650 :         psInfo->m_nFeaturesRead++;
    6222             : 
    6223        4650 :         int nIters = 1;
    6224           0 :         std::unique_ptr<OGRGeometryCollection> poCollToExplode;
    6225        4650 :         int iGeomCollToExplode = -1;
    6226        4650 :         OGRGeometry *poSrcGeometry = nullptr;
    6227        4650 :         if (bExplodeCollections)
    6228             :         {
    6229          13 :             if (iRequestedSrcGeomField >= 0)
    6230             :                 poSrcGeometry =
    6231           0 :                     poFeature->GetGeomFieldRef(iRequestedSrcGeomField);
    6232             :             else
    6233          13 :                 poSrcGeometry = poFeature->GetGeometryRef();
    6234          26 :             if (poSrcGeometry &&
    6235          13 :                 OGR_GT_IsSubClassOf(poSrcGeometry->getGeometryType(),
    6236             :                                     wkbGeometryCollection))
    6237             :             {
    6238             :                 const int nParts =
    6239          12 :                     poSrcGeometry->toGeometryCollection()->getNumGeometries();
    6240          21 :                 if (nParts > 0 ||
    6241           9 :                     wkbFlatten(poSrcGeometry->getGeometryType()) !=
    6242             :                         wkbGeometryCollection)
    6243             :                 {
    6244          11 :                     iGeomCollToExplode = iRequestedSrcGeomField >= 0
    6245             :                                              ? iRequestedSrcGeomField
    6246             :                                              : 0;
    6247          11 :                     poCollToExplode.reset(
    6248             :                         poFeature->StealGeometry(iGeomCollToExplode)
    6249             :                             ->toGeometryCollection());
    6250          11 :                     nIters = std::max(1, nParts);
    6251             :                 }
    6252             :             }
    6253             :         }
    6254             : 
    6255        4650 :         const GIntBig nSrcFID = poFeature->GetFID();
    6256        4650 :         GIntBig nDesiredFID = OGRNullFID;
    6257        4650 :         if (bPreserveFID)
    6258        1160 :             nDesiredFID = nSrcFID;
    6259        3491 :         else if (psInfo->m_iSrcFIDField >= 0 &&
    6260           1 :                  poFeature->IsFieldSetAndNotNull(psInfo->m_iSrcFIDField))
    6261             :             nDesiredFID =
    6262           1 :                 poFeature->GetFieldAsInteger64(psInfo->m_iSrcFIDField);
    6263             : 
    6264        9299 :         for (int iPart = 0; iPart < nIters; iPart++)
    6265             :         {
    6266        7944 :             if (psOptions->nLayerTransaction &&
    6267        3291 :                 ++nFeaturesInTransaction == psOptions->nGroupTransactions)
    6268             :             {
    6269           0 :                 if (poDstLayer->CommitTransaction() == OGRERR_FAILURE ||
    6270           0 :                     poDstLayer->StartTransaction() == OGRERR_FAILURE)
    6271             :                 {
    6272           0 :                     return false;
    6273             :                 }
    6274           0 :                 nFeaturesInTransaction = 0;
    6275             :             }
    6276       10668 :             else if (!psOptions->nLayerTransaction &&
    6277        5995 :                      psOptions->nGroupTransactions > 0 &&
    6278        1342 :                      ++nTotalEventsDone >= psOptions->nGroupTransactions)
    6279             :             {
    6280          40 :                 if (m_poODS->CommitTransaction() == OGRERR_FAILURE ||
    6281          20 :                     m_poODS->StartTransaction(psOptions->bForceTransaction) ==
    6282             :                         OGRERR_FAILURE)
    6283             :                 {
    6284           0 :                     return false;
    6285             :                 }
    6286          20 :                 nTotalEventsDone = 0;
    6287             :             }
    6288             : 
    6289        4653 :             CPLErrorReset();
    6290        4653 :             if (psInfo->m_bCanAvoidSetFrom)
    6291             :             {
    6292        4366 :                 poDstFeature = std::move(poFeature);
    6293             :                 // From now on, poFeature is null !
    6294        4366 :                 poDstFeature->SetFDefnUnsafe(poDstFDefn);
    6295        4366 :                 poDstFeature->SetFID(nDesiredFID);
    6296             :             }
    6297             :             else
    6298             :             {
    6299             :                 /* Optimization to avoid duplicating the source geometry in the
    6300             :                  */
    6301             :                 /* target feature : we steal it from the source feature for
    6302             :                  * now... */
    6303           0 :                 std::unique_ptr<OGRGeometry> poStolenGeometry;
    6304         287 :                 if (!bExplodeCollections && nSrcGeomFieldCount == 1 &&
    6305          64 :                     (nDstGeomFieldCount == 1 ||
    6306          64 :                      (nDstGeomFieldCount == 0 && m_poClipSrcOri)))
    6307             :                 {
    6308         183 :                     poStolenGeometry.reset(poFeature->StealGeometry());
    6309             :                 }
    6310         104 :                 else if (!bExplodeCollections && iRequestedSrcGeomField >= 0)
    6311             :                 {
    6312           0 :                     poStolenGeometry.reset(
    6313             :                         poFeature->StealGeometry(iRequestedSrcGeomField));
    6314             :                 }
    6315             : 
    6316         287 :                 if (nDstGeomFieldCount == 0 && poStolenGeometry &&
    6317           0 :                     m_poClipSrcOri)
    6318             :                 {
    6319           0 :                     if (poStolenGeometry->IsEmpty())
    6320           0 :                         goto end_loop;
    6321             : 
    6322           0 :                     const auto [poClipGeom, poClipGeomEnvelope] =
    6323           0 :                         GetSrcClipGeom(poStolenGeometry->getSpatialReference());
    6324             : 
    6325           0 :                     if (poClipGeom && poClipGeomEnvelope)
    6326             :                     {
    6327           0 :                         OGREnvelope oEnv;
    6328           0 :                         poStolenGeometry->getEnvelope(&oEnv);
    6329           0 :                         if (!poClipGeomEnvelope->Contains(oEnv) &&
    6330           0 :                             !(poClipGeomEnvelope->Intersects(oEnv) &&
    6331           0 :                               poClipGeom->Intersects(poStolenGeometry.get())))
    6332             :                         {
    6333           0 :                             goto end_loop;
    6334             :                         }
    6335             :                     }
    6336             :                 }
    6337             : 
    6338         287 :                 poDstFeature->Reset();
    6339         287 :                 if (poDstFeature->SetFrom(poFeature.get(), panMap, TRUE) !=
    6340             :                     OGRERR_NONE)
    6341             :                 {
    6342           0 :                     if (psOptions->nGroupTransactions)
    6343             :                     {
    6344           0 :                         if (psOptions->nLayerTransaction)
    6345             :                         {
    6346           0 :                             if (poDstLayer->CommitTransaction() != OGRERR_NONE)
    6347             :                             {
    6348           0 :                                 return false;
    6349             :                             }
    6350             :                         }
    6351             :                     }
    6352             : 
    6353           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    6354             :                              "Unable to translate feature " CPL_FRMT_GIB
    6355             :                              " from layer %s.",
    6356           0 :                              nSrcFID, poSrcLayer->GetName());
    6357             : 
    6358           0 :                     return false;
    6359             :                 }
    6360             : 
    6361             :                 /* ... and now we can attach the stolen geometry */
    6362         287 :                 if (poStolenGeometry)
    6363             :                 {
    6364         176 :                     poDstFeature->SetGeometryDirectly(
    6365             :                         poStolenGeometry.release());
    6366             :                 }
    6367             : 
    6368         287 :                 if (!psInfo->m_oMapResolved.empty())
    6369             :                 {
    6370           4 :                     for (const auto &kv : psInfo->m_oMapResolved)
    6371             :                     {
    6372           2 :                         const int nDstField = kv.first;
    6373           2 :                         const int nSrcField = kv.second.nSrcField;
    6374           2 :                         if (poFeature->IsFieldSetAndNotNull(nSrcField))
    6375             :                         {
    6376           2 :                             const auto poDomain = kv.second.poDomain;
    6377             :                             const auto &oMapKV =
    6378           2 :                                 psInfo->m_oMapDomainToKV[poDomain];
    6379             :                             const auto iter = oMapKV.find(
    6380           2 :                                 poFeature->GetFieldAsString(nSrcField));
    6381           2 :                             if (iter != oMapKV.end())
    6382             :                             {
    6383           2 :                                 poDstFeature->SetField(nDstField,
    6384           1 :                                                        iter->second.c_str());
    6385             :                             }
    6386             :                         }
    6387             :                     }
    6388             :                 }
    6389             : 
    6390         287 :                 if (nDesiredFID != OGRNullFID)
    6391           2 :                     poDstFeature->SetFID(nDesiredFID);
    6392             :             }
    6393             : 
    6394        4653 :             if (psOptions->bEmptyStrAsNull)
    6395             :             {
    6396           2 :                 for (int i = 0; i < poDstFeature->GetFieldCount(); i++)
    6397             :                 {
    6398           1 :                     if (!poDstFeature->IsFieldSetAndNotNull(i))
    6399           0 :                         continue;
    6400           1 :                     auto fieldDef = poDstFeature->GetFieldDefnRef(i);
    6401           1 :                     if (fieldDef->GetType() != OGRFieldType::OFTString)
    6402           0 :                         continue;
    6403           1 :                     auto str = poDstFeature->GetFieldAsString(i);
    6404           1 :                     if (strcmp(str, "") == 0)
    6405           1 :                         poDstFeature->SetFieldNull(i);
    6406             :                 }
    6407             :             }
    6408             : 
    6409        4653 :             if (!psInfo->m_anDateTimeFieldIdx.empty())
    6410             :             {
    6411          40 :                 for (int i : psInfo->m_anDateTimeFieldIdx)
    6412             :                 {
    6413          20 :                     if (!poDstFeature->IsFieldSetAndNotNull(i))
    6414          11 :                         continue;
    6415          15 :                     auto psField = poDstFeature->GetRawFieldRef(i);
    6416          15 :                     if (psField->Date.TZFlag == 0 || psField->Date.TZFlag == 1)
    6417           5 :                         continue;
    6418             : 
    6419          10 :                     const int nTZOffsetInSec =
    6420          10 :                         (psField->Date.TZFlag - 100) * 15 * 60;
    6421          10 :                     if (nTZOffsetInSec == psOptions->nTZOffsetInSec)
    6422           1 :                         continue;
    6423             : 
    6424             :                     struct tm brokendowntime;
    6425           9 :                     memset(&brokendowntime, 0, sizeof(brokendowntime));
    6426           9 :                     brokendowntime.tm_year = psField->Date.Year - 1900;
    6427           9 :                     brokendowntime.tm_mon = psField->Date.Month - 1;
    6428           9 :                     brokendowntime.tm_mday = psField->Date.Day;
    6429           9 :                     GIntBig nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime);
    6430           9 :                     int nSec = psField->Date.Hour * 3600 +
    6431           9 :                                psField->Date.Minute * 60 +
    6432           9 :                                static_cast<int>(psField->Date.Second);
    6433           9 :                     nSec += psOptions->nTZOffsetInSec - nTZOffsetInSec;
    6434           9 :                     nUnixTime += nSec;
    6435           9 :                     CPLUnixTimeToYMDHMS(nUnixTime, &brokendowntime);
    6436             : 
    6437           9 :                     psField->Date.Year =
    6438           9 :                         static_cast<GInt16>(brokendowntime.tm_year + 1900);
    6439           9 :                     psField->Date.Month =
    6440           9 :                         static_cast<GByte>(brokendowntime.tm_mon + 1);
    6441           9 :                     psField->Date.Day =
    6442           9 :                         static_cast<GByte>(brokendowntime.tm_mday);
    6443           9 :                     psField->Date.Hour =
    6444           9 :                         static_cast<GByte>(brokendowntime.tm_hour);
    6445           9 :                     psField->Date.Minute =
    6446           9 :                         static_cast<GByte>(brokendowntime.tm_min);
    6447           9 :                     psField->Date.Second = static_cast<float>(
    6448           9 :                         brokendowntime.tm_sec + fmod(psField->Date.Second, 1));
    6449           9 :                     psField->Date.TZFlag = static_cast<GByte>(
    6450           9 :                         100 + psOptions->nTZOffsetInSec / (15 * 60));
    6451             :                 }
    6452             :             }
    6453             : 
    6454             :             /* Erase native data if asked explicitly */
    6455        4653 :             if (!m_bNativeData)
    6456             :             {
    6457           1 :                 poDstFeature->SetNativeData(nullptr);
    6458           1 :                 poDstFeature->SetNativeMediaType(nullptr);
    6459             :             }
    6460             : 
    6461        8243 :             for (int iGeom = 0; iGeom < nDstGeomFieldCount; iGeom++)
    6462             :             {
    6463           0 :                 std::unique_ptr<OGRGeometry> poDstGeometry;
    6464             : 
    6465        3635 :                 if (poCollToExplode && iGeom == iGeomCollToExplode)
    6466             :                 {
    6467          14 :                     if (poSrcGeometry && poCollToExplode->IsEmpty())
    6468             :                     {
    6469             :                         const OGRwkbGeometryType eSrcType =
    6470           8 :                             poSrcGeometry->getGeometryType();
    6471             :                         const OGRwkbGeometryType eSrcFlattenType =
    6472           8 :                             wkbFlatten(eSrcType);
    6473           8 :                         OGRwkbGeometryType eDstType = eSrcType;
    6474           8 :                         switch (eSrcFlattenType)
    6475             :                         {
    6476           4 :                             case wkbMultiPoint:
    6477           4 :                                 eDstType = wkbPoint;
    6478           4 :                                 break;
    6479           1 :                             case wkbMultiLineString:
    6480           1 :                                 eDstType = wkbLineString;
    6481           1 :                                 break;
    6482           1 :                             case wkbMultiPolygon:
    6483           1 :                                 eDstType = wkbPolygon;
    6484           1 :                                 break;
    6485           1 :                             case wkbMultiCurve:
    6486           1 :                                 eDstType = wkbCompoundCurve;
    6487           1 :                                 break;
    6488           1 :                             case wkbMultiSurface:
    6489           1 :                                 eDstType = wkbCurvePolygon;
    6490           1 :                                 break;
    6491           0 :                             default:
    6492           0 :                                 break;
    6493             :                         }
    6494             :                         eDstType =
    6495           8 :                             OGR_GT_SetModifier(eDstType, OGR_GT_HasZ(eSrcType),
    6496             :                                                OGR_GT_HasM(eSrcType));
    6497           8 :                         poDstGeometry.reset(
    6498             :                             OGRGeometryFactory::createGeometry(eDstType));
    6499             :                     }
    6500             :                     else
    6501             :                     {
    6502             :                         OGRGeometry *poPart =
    6503           6 :                             poCollToExplode->getGeometryRef(0);
    6504           6 :                         poCollToExplode->removeGeometry(0, FALSE);
    6505           6 :                         poDstGeometry.reset(poPart);
    6506             :                     }
    6507             :                 }
    6508             :                 else
    6509             :                 {
    6510        3621 :                     poDstGeometry.reset(poDstFeature->StealGeometry(iGeom));
    6511             :                 }
    6512        3635 :                 if (poDstGeometry == nullptr)
    6513         612 :                     continue;
    6514             : 
    6515             :                 // poFeature hasn't been moved if iSrcZField != -1
    6516             :                 // cppcheck-suppress accessMoved
    6517        3023 :                 if (iSrcZField != -1 && poFeature != nullptr)
    6518             :                 {
    6519          30 :                     SetZ(poDstGeometry.get(),
    6520             :                          poFeature->GetFieldAsDouble(iSrcZField));
    6521             :                     /* This will correct the coordinate dimension to 3 */
    6522          30 :                     poDstGeometry.reset(poDstGeometry->clone());
    6523             :                 }
    6524             : 
    6525        3023 :                 if (m_nCoordDim == 2 || m_nCoordDim == 3)
    6526             :                 {
    6527          24 :                     poDstGeometry->setCoordinateDimension(m_nCoordDim);
    6528             :                 }
    6529        2999 :                 else if (m_nCoordDim == 4)
    6530             :                 {
    6531           2 :                     poDstGeometry->set3D(TRUE);
    6532           2 :                     poDstGeometry->setMeasured(TRUE);
    6533             :                 }
    6534        2997 :                 else if (m_nCoordDim == COORD_DIM_XYM)
    6535             :                 {
    6536           2 :                     poDstGeometry->set3D(FALSE);
    6537           2 :                     poDstGeometry->setMeasured(TRUE);
    6538             :                 }
    6539        2995 :                 else if (m_nCoordDim == COORD_DIM_LAYER_DIM)
    6540             :                 {
    6541             :                     const OGRwkbGeometryType eDstLayerGeomType =
    6542           2 :                         poDstLayer->GetLayerDefn()
    6543           2 :                             ->GetGeomFieldDefn(iGeom)
    6544           2 :                             ->GetType();
    6545           2 :                     poDstGeometry->set3D(wkbHasZ(eDstLayerGeomType));
    6546           2 :                     poDstGeometry->setMeasured(wkbHasM(eDstLayerGeomType));
    6547             :                 }
    6548             : 
    6549        3023 :                 if (m_eGeomOp == GEOMOP_SEGMENTIZE)
    6550             :                 {
    6551          20 :                     if (m_dfGeomOpParam > 0)
    6552          20 :                         poDstGeometry->segmentize(m_dfGeomOpParam);
    6553             :                 }
    6554        3003 :                 else if (m_eGeomOp == GEOMOP_SIMPLIFY_PRESERVE_TOPOLOGY)
    6555             :                 {
    6556           1 :                     if (m_dfGeomOpParam > 0)
    6557             :                     {
    6558             :                         auto poNewGeom = std::unique_ptr<OGRGeometry>(
    6559             :                             poDstGeometry->SimplifyPreserveTopology(
    6560           2 :                                 m_dfGeomOpParam));
    6561           1 :                         if (poNewGeom)
    6562             :                         {
    6563           1 :                             poDstGeometry = std::move(poNewGeom);
    6564             :                         }
    6565             :                     }
    6566             :                 }
    6567             : 
    6568        3023 :                 if (m_poClipSrcOri)
    6569             :                 {
    6570          45 :                     if (poDstGeometry->IsEmpty())
    6571          25 :                         goto end_loop;
    6572             : 
    6573          45 :                     const auto [poClipGeom, poClipGeomEnvelope] =
    6574          45 :                         GetSrcClipGeom(poDstGeometry->getSpatialReference());
    6575             : 
    6576          45 :                     if (!(poClipGeom && poClipGeomEnvelope))
    6577           0 :                         goto end_loop;
    6578             : 
    6579          45 :                     OGREnvelope oDstEnv;
    6580          45 :                     poDstGeometry->getEnvelope(&oDstEnv);
    6581             : 
    6582          45 :                     if (!poClipGeomEnvelope->Contains(oDstEnv))
    6583             :                     {
    6584           0 :                         std::unique_ptr<OGRGeometry> poClipped;
    6585          38 :                         if (poClipGeomEnvelope->Intersects(oDstEnv))
    6586             :                         {
    6587          18 :                             poClipped.reset(
    6588          18 :                                 poClipGeom->Intersection(poDstGeometry.get()));
    6589             :                         }
    6590          38 :                         if (poClipped == nullptr || poClipped->IsEmpty())
    6591             :                         {
    6592          24 :                             goto end_loop;
    6593             :                         }
    6594             : 
    6595          14 :                         const int nDim = poDstGeometry->getDimension();
    6596          15 :                         if (poClipped->getDimension() < nDim &&
    6597           1 :                             wkbFlatten(poDstFDefn->GetGeomFieldDefn(iGeom)
    6598             :                                            ->GetType()) != wkbUnknown)
    6599             :                         {
    6600           3 :                             CPLDebug(
    6601             :                                 "OGR2OGR",
    6602             :                                 "Discarding feature " CPL_FRMT_GIB
    6603             :                                 " of layer %s, "
    6604             :                                 "as its intersection with -clipsrc is a %s "
    6605             :                                 "whereas the input is a %s",
    6606           1 :                                 nSrcFID, poSrcLayer->GetName(),
    6607           1 :                                 OGRToOGCGeomType(poClipped->getGeometryType()),
    6608             :                                 OGRToOGCGeomType(
    6609           1 :                                     poDstGeometry->getGeometryType()));
    6610           1 :                             goto end_loop;
    6611             :                         }
    6612             : 
    6613          13 :                         poDstGeometry = std::move(poClipped);
    6614             :                     }
    6615             :                 }
    6616             : 
    6617             :                 OGRCoordinateTransformation *const poCT =
    6618        2998 :                     psInfo->m_aoReprojectionInfo[iGeom].m_poCT.get();
    6619             :                 char **const papszTransformOptions =
    6620        2998 :                     psInfo->m_aoReprojectionInfo[iGeom]
    6621        2998 :                         .m_aosTransformOptions.List();
    6622             :                 const bool bReprojCanInvalidateValidity =
    6623        2998 :                     psInfo->m_aoReprojectionInfo[iGeom]
    6624        2998 :                         .m_bCanInvalidateValidity;
    6625             : 
    6626        2998 :                 if (poCT != nullptr || papszTransformOptions != nullptr)
    6627             :                 {
    6628             :                     // If we need to change the geometry type to linear, and
    6629             :                     // we have a geometry with curves, then convert it to
    6630             :                     // linear first, to avoid invalidities due to the fact
    6631             :                     // that validity of arc portions isn't always kept while
    6632             :                     // reprojecting and then discretizing.
    6633         113 :                     if (bReprojCanInvalidateValidity &&
    6634         111 :                         (!psInfo->m_bSupportCurves ||
    6635          50 :                          m_eGeomTypeConversion == GTC_CONVERT_TO_LINEAR ||
    6636          48 :                          m_eGeomTypeConversion ==
    6637             :                              GTC_PROMOTE_TO_MULTI_AND_CONVERT_TO_LINEAR))
    6638             :                     {
    6639          63 :                         if (poDstGeometry->hasCurveGeometry(TRUE))
    6640             :                         {
    6641           4 :                             OGRwkbGeometryType eTargetType = OGR_GT_GetLinear(
    6642           4 :                                 poDstGeometry->getGeometryType());
    6643           4 :                             poDstGeometry.reset(OGRGeometryFactory::forceTo(
    6644             :                                 poDstGeometry.release(), eTargetType));
    6645          63 :                         }
    6646             :                     }
    6647          48 :                     else if (bReprojCanInvalidateValidity &&
    6648           2 :                              eGType != GEOMTYPE_UNCHANGED &&
    6649           2 :                              !OGR_GT_IsNonLinear(
    6650          98 :                                  static_cast<OGRwkbGeometryType>(eGType)) &&
    6651           2 :                              poDstGeometry->hasCurveGeometry(TRUE))
    6652             :                     {
    6653           2 :                         poDstGeometry.reset(OGRGeometryFactory::forceTo(
    6654             :                             poDstGeometry.release(),
    6655             :                             static_cast<OGRwkbGeometryType>(eGType)));
    6656             :                     }
    6657             : 
    6658         114 :                     for (int iIter = 0; iIter < 2; ++iIter)
    6659             :                     {
    6660             :                         auto poReprojectedGeom = std::unique_ptr<OGRGeometry>(
    6661             :                             OGRGeometryFactory::transformWithOptions(
    6662         114 :                                 poDstGeometry.get(), poCT,
    6663             :                                 papszTransformOptions,
    6664         114 :                                 m_transformWithOptionsCache));
    6665         114 :                         if (poReprojectedGeom == nullptr)
    6666             :                         {
    6667           0 :                             if (psOptions->nGroupTransactions)
    6668             :                             {
    6669           0 :                                 if (psOptions->nLayerTransaction)
    6670             :                                 {
    6671           0 :                                     if (poDstLayer->CommitTransaction() !=
    6672           0 :                                             OGRERR_NONE &&
    6673           0 :                                         !psOptions->bSkipFailures)
    6674             :                                     {
    6675           0 :                                         return false;
    6676             :                                     }
    6677             :                                 }
    6678             :                             }
    6679             : 
    6680           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    6681             :                                      "Failed to reproject feature " CPL_FRMT_GIB
    6682             :                                      " (geometry probably out of source or "
    6683             :                                      "destination SRS).",
    6684             :                                      nSrcFID);
    6685           0 :                             if (!psOptions->bSkipFailures)
    6686             :                             {
    6687           0 :                                 return false;
    6688             :                             }
    6689             :                         }
    6690             : 
    6691             :                         // Check if a curve geometry is no longer valid after
    6692             :                         // reprojection
    6693         114 :                         const auto eType = poDstGeometry->getGeometryType();
    6694         114 :                         const auto eFlatType = wkbFlatten(eType);
    6695             : 
    6696           4 :                         const auto IsValid = [](const OGRGeometry *poGeom)
    6697             :                         {
    6698             :                             CPLErrorHandlerPusher oErrorHandler(
    6699           8 :                                 CPLQuietErrorHandler);
    6700           8 :                             return poGeom->IsValid();
    6701             :                         };
    6702             : 
    6703         113 :                         if (iIter == 0 && bReprojCanInvalidateValidity &&
    6704         111 :                             OGRGeometryFactory::haveGEOS() &&
    6705         109 :                             (eFlatType == wkbCurvePolygon ||
    6706         109 :                              eFlatType == wkbCompoundCurve ||
    6707         109 :                              eFlatType == wkbMultiCurve ||
    6708           2 :                              eFlatType == wkbMultiSurface) &&
    6709         229 :                             poDstGeometry->hasCurveGeometry(TRUE) &&
    6710           2 :                             IsValid(poDstGeometry.get()))
    6711             :                         {
    6712           2 :                             OGRwkbGeometryType eTargetType = OGR_GT_GetLinear(
    6713           2 :                                 poDstGeometry->getGeometryType());
    6714             :                             auto poDstGeometryTmp =
    6715             :                                 std::unique_ptr<OGRGeometry>(
    6716             :                                     OGRGeometryFactory::forceTo(
    6717           2 :                                         poReprojectedGeom->clone(),
    6718           2 :                                         eTargetType));
    6719           2 :                             if (!IsValid(poDstGeometryTmp.get()))
    6720             :                             {
    6721           1 :                                 CPLDebug("OGR2OGR",
    6722             :                                          "Curve geometry no longer valid after "
    6723             :                                          "reprojection: transforming it into "
    6724             :                                          "linear one before reprojecting");
    6725           1 :                                 poDstGeometry.reset(OGRGeometryFactory::forceTo(
    6726             :                                     poDstGeometry.release(), eTargetType));
    6727           1 :                                 poDstGeometry.reset(OGRGeometryFactory::forceTo(
    6728             :                                     poDstGeometry.release(), eType));
    6729             :                             }
    6730             :                             else
    6731             :                             {
    6732           1 :                                 poDstGeometry = std::move(poReprojectedGeom);
    6733           1 :                                 break;
    6734             :                             }
    6735             :                         }
    6736             :                         else
    6737             :                         {
    6738         112 :                             poDstGeometry = std::move(poReprojectedGeom);
    6739         112 :                             break;
    6740             :                         }
    6741         113 :                     }
    6742             :                 }
    6743        2885 :                 else if (poOutputSRS != nullptr)
    6744             :                 {
    6745        1843 :                     poDstGeometry->assignSpatialReference(poOutputSRS);
    6746             :                 }
    6747             : 
    6748        2998 :                 if (poDstGeometry != nullptr)
    6749             :                 {
    6750        2998 :                     if (m_poClipDstOri)
    6751             :                     {
    6752          39 :                         if (poDstGeometry->IsEmpty())
    6753          19 :                             goto end_loop;
    6754             : 
    6755          39 :                         auto [poClipGeom, poClipGeomEnvelope] = GetDstClipGeom(
    6756          39 :                             poDstGeometry->getSpatialReference());
    6757          39 :                         if (!poClipGeom || !poClipGeomEnvelope)
    6758             :                         {
    6759           0 :                             goto end_loop;
    6760             :                         }
    6761             : 
    6762          39 :                         OGREnvelope oDstEnv;
    6763          39 :                         poDstGeometry->getEnvelope(&oDstEnv);
    6764             : 
    6765          39 :                         if (!poClipGeomEnvelope->Contains(oDstEnv))
    6766             :                         {
    6767           0 :                             std::unique_ptr<OGRGeometry> poClipped;
    6768          31 :                             if (poClipGeomEnvelope->Intersects(oDstEnv))
    6769             :                             {
    6770          16 :                                 poClipped.reset(poClipGeom->Intersection(
    6771          16 :                                     poDstGeometry.get()));
    6772             :                             }
    6773             : 
    6774          31 :                             if (poClipped == nullptr || poClipped->IsEmpty())
    6775             :                             {
    6776          18 :                                 goto end_loop;
    6777             :                             }
    6778             : 
    6779          13 :                             const int nDim = poDstGeometry->getDimension();
    6780          14 :                             if (poClipped->getDimension() < nDim &&
    6781           1 :                                 wkbFlatten(poDstFDefn->GetGeomFieldDefn(iGeom)
    6782             :                                                ->GetType()) != wkbUnknown)
    6783             :                             {
    6784           3 :                                 CPLDebug(
    6785             :                                     "OGR2OGR",
    6786             :                                     "Discarding feature " CPL_FRMT_GIB
    6787             :                                     " of layer %s, "
    6788             :                                     "as its intersection with -clipdst is a %s "
    6789             :                                     "whereas the input is a %s",
    6790           1 :                                     nSrcFID, poSrcLayer->GetName(),
    6791             :                                     OGRToOGCGeomType(
    6792           1 :                                         poClipped->getGeometryType()),
    6793             :                                     OGRToOGCGeomType(
    6794           1 :                                         poDstGeometry->getGeometryType()));
    6795           1 :                                 goto end_loop;
    6796             :                             }
    6797             : 
    6798          12 :                             poDstGeometry = std::move(poClipped);
    6799             :                         }
    6800             :                     }
    6801             : 
    6802        5958 :                     if (psOptions->dfXYRes !=
    6803           1 :                             OGRGeomCoordinatePrecision::UNKNOWN &&
    6804        2980 :                         OGRGeometryFactory::haveGEOS() &&
    6805           1 :                         !poDstGeometry->hasCurveGeometry())
    6806             :                     {
    6807             :                         // OGR_APPLY_GEOM_SET_PRECISION default value for
    6808             :                         // OGRLayer::CreateFeature() purposes, but here in the
    6809             :                         // ogr2ogr -xyRes context, we force calling SetPrecision(),
    6810             :                         // unless the user explicitly asks not to do it by
    6811             :                         // setting the config option to NO.
    6812           1 :                         if (!bRunSetPrecisionEvaluated)
    6813             :                         {
    6814           1 :                             bRunSetPrecisionEvaluated = true;
    6815           1 :                             bRunSetPrecision = CPLTestBool(CPLGetConfigOption(
    6816             :                                 "OGR_APPLY_GEOM_SET_PRECISION", "YES"));
    6817             :                         }
    6818           1 :                         if (bRunSetPrecision)
    6819             :                         {
    6820             :                             auto poNewGeom = std::unique_ptr<OGRGeometry>(
    6821           1 :                                 poDstGeometry->SetPrecision(psOptions->dfXYRes,
    6822           1 :                                                             /* nFlags = */ 0));
    6823           1 :                             if (!poNewGeom)
    6824           0 :                                 goto end_loop;
    6825           1 :                             poDstGeometry = std::move(poNewGeom);
    6826             :                         }
    6827             :                     }
    6828             : 
    6829        2979 :                     if (m_bMakeValid)
    6830             :                     {
    6831             :                         const bool bIsGeomCollection =
    6832           7 :                             wkbFlatten(poDstGeometry->getGeometryType()) ==
    6833           7 :                             wkbGeometryCollection;
    6834             :                         auto poNewGeom = std::unique_ptr<OGRGeometry>(
    6835           7 :                             poDstGeometry->MakeValid());
    6836           7 :                         if (!poNewGeom)
    6837           0 :                             goto end_loop;
    6838           7 :                         poDstGeometry = std::move(poNewGeom);
    6839           7 :                         if (!bIsGeomCollection)
    6840             :                         {
    6841           6 :                             poDstGeometry.reset(
    6842             :                                 OGRGeometryFactory::
    6843             :                                     removeLowerDimensionSubGeoms(
    6844           6 :                                         poDstGeometry.get()));
    6845             :                         }
    6846             :                     }
    6847             : 
    6848        2979 :                     if (m_bSkipInvalidGeom && !poDstGeometry->IsValid())
    6849           1 :                         goto end_loop;
    6850             : 
    6851        2978 :                     if (m_eGeomTypeConversion != GTC_DEFAULT)
    6852             :                     {
    6853             :                         OGRwkbGeometryType eTargetType =
    6854          11 :                             poDstGeometry->getGeometryType();
    6855             :                         eTargetType =
    6856          11 :                             ConvertType(m_eGeomTypeConversion, eTargetType);
    6857          11 :                         poDstGeometry.reset(OGRGeometryFactory::forceTo(
    6858             :                             poDstGeometry.release(), eTargetType));
    6859             :                     }
    6860        2967 :                     else if (eGType != GEOMTYPE_UNCHANGED)
    6861             :                     {
    6862          59 :                         poDstGeometry.reset(OGRGeometryFactory::forceTo(
    6863             :                             poDstGeometry.release(),
    6864             :                             static_cast<OGRwkbGeometryType>(eGType)));
    6865             :                     }
    6866             :                 }
    6867             : 
    6868        2978 :                 poDstFeature->SetGeomFieldDirectly(iGeom,
    6869             :                                                    poDstGeometry.release());
    6870             :             }
    6871             : 
    6872        4608 :             CPLErrorReset();
    6873        9216 :             if ((psOptions->bUpsert
    6874        4608 :                      ? poDstLayer->UpsertFeature(poDstFeature.get())
    6875        4608 :                      : poDstLayer->CreateFeature(poDstFeature.get())) ==
    6876             :                 OGRERR_NONE)
    6877             :             {
    6878        4603 :                 nFeaturesWritten++;
    6879        5758 :                 if (nDesiredFID != OGRNullFID &&
    6880        1155 :                     poDstFeature->GetFID() != nDesiredFID)
    6881             :                 {
    6882           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    6883             :                              "Feature id " CPL_FRMT_GIB " not preserved",
    6884             :                              nDesiredFID);
    6885             :                 }
    6886             :             }
    6887           5 :             else if (!psOptions->bSkipFailures)
    6888             :             {
    6889           4 :                 if (psOptions->nGroupTransactions)
    6890             :                 {
    6891           4 :                     if (psOptions->nLayerTransaction)
    6892           0 :                         poDstLayer->RollbackTransaction();
    6893             :                 }
    6894             : 
    6895           4 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6896             :                          "Unable to write feature " CPL_FRMT_GIB
    6897             :                          " from layer %s.",
    6898           4 :                          nSrcFID, poSrcLayer->GetName());
    6899             : 
    6900           4 :                 return false;
    6901             :             }
    6902             :             else
    6903             :             {
    6904           1 :                 CPLDebug("GDALVectorTranslate",
    6905             :                          "Unable to write feature " CPL_FRMT_GIB
    6906             :                          " into layer %s.",
    6907           1 :                          nSrcFID, poSrcLayer->GetName());
    6908           1 :                 if (psOptions->nGroupTransactions)
    6909             :                 {
    6910           1 :                     if (psOptions->nLayerTransaction)
    6911             :                     {
    6912           0 :                         poDstLayer->RollbackTransaction();
    6913           0 :                         CPL_IGNORE_RET_VAL(poDstLayer->StartTransaction());
    6914             :                     }
    6915             :                     else
    6916             :                     {
    6917           1 :                         m_poODS->RollbackTransaction();
    6918           1 :                         m_poODS->StartTransaction(psOptions->bForceTransaction);
    6919             :                     }
    6920             :                 }
    6921             :             }
    6922             : 
    6923        4649 :         end_loop:;  // nothing
    6924             :         }
    6925             : 
    6926             :         /* Report progress */
    6927        4646 :         nCount++;
    6928        4646 :         bool bGoOn = true;
    6929        4646 :         if (pfnProgress)
    6930             :         {
    6931          55 :             bGoOn = pfnProgress(nCountLayerFeatures
    6932          26 :                                     ? nCount * 1.0 / nCountLayerFeatures
    6933             :                                     : 1.0,
    6934             :                                 "", pProgressArg) != FALSE;
    6935             :         }
    6936        4646 :         if (!bGoOn)
    6937             :         {
    6938           1 :             bRet = false;
    6939           1 :             break;
    6940             :         }
    6941             : 
    6942        4645 :         if (pnReadFeatureCount)
    6943           0 :             *pnReadFeatureCount = nCount;
    6944             : 
    6945        4645 :         if (psOptions->nFIDToFetch != OGRNullFID)
    6946           5 :             break;
    6947        4640 :         if (poFeatureIn != nullptr)
    6948         974 :             break;
    6949        3666 :     }
    6950             : 
    6951        1526 :     if (psOptions->nGroupTransactions)
    6952             :     {
    6953        1525 :         if (psOptions->nLayerTransaction)
    6954             :         {
    6955         463 :             if (poDstLayer->CommitTransaction() != OGRERR_NONE)
    6956           0 :                 bRet = false;
    6957             :         }
    6958             :     }
    6959             : 
    6960        1526 :     if (poFeatureIn == nullptr)
    6961             :     {
    6962         552 :         CPLDebug("GDALVectorTranslate",
    6963             :                  CPL_FRMT_GIB " features written in layer '%s'",
    6964         552 :                  nFeaturesWritten, poDstLayer->GetName());
    6965             :     }
    6966             : 
    6967        1526 :     return bRet;
    6968             : }
    6969             : 
    6970             : /************************************************************************/
    6971             : /*                LayerTranslator::GetDstClipGeom()                     */
    6972             : /************************************************************************/
    6973             : 
    6974             : /** Returns the destination clip geometry and its envelope
    6975             :  *
    6976             :  * @param poGeomSRS The SRS into which the destination clip geometry should be
    6977             :  *                  expressed.
    6978             :  * @return the destination clip geometry and its envelope, or (nullptr, nullptr)
    6979             :  */
    6980             : std::pair<const OGRGeometry *, const OGREnvelope *>
    6981          39 : LayerTranslator::GetDstClipGeom(const OGRSpatialReference *poGeomSRS)
    6982             : {
    6983          39 :     if (m_poClipDstReprojectedToDstSRS_SRS != poGeomSRS)
    6984             :     {
    6985          36 :         auto poClipDstSRS = m_poClipDstOri->getSpatialReference();
    6986          36 :         if (poClipDstSRS && poGeomSRS && !poClipDstSRS->IsSame(poGeomSRS))
    6987             :         {
    6988             :             // Transform clip geom to geometry SRS
    6989           1 :             m_poClipDstReprojectedToDstSRS.reset(m_poClipDstOri->clone());
    6990           1 :             if (m_poClipDstReprojectedToDstSRS->transformTo(poGeomSRS) !=
    6991             :                 OGRERR_NONE)
    6992             :             {
    6993           0 :                 return std::make_pair(nullptr, nullptr);
    6994             :             }
    6995           1 :             m_poClipDstReprojectedToDstSRS_SRS = poGeomSRS;
    6996             :         }
    6997          35 :         else if (!poClipDstSRS && poGeomSRS)
    6998             :         {
    6999          35 :             if (!m_bWarnedClipDstSRS)
    7000             :             {
    7001           2 :                 m_bWarnedClipDstSRS = true;
    7002           2 :                 CPLError(CE_Warning, CPLE_AppDefined,
    7003             :                          "Clip destination geometry has no "
    7004             :                          "attached SRS, but the feature's "
    7005             :                          "geometry has one. Assuming clip "
    7006             :                          "destination geometry SRS is the "
    7007             :                          "same as the feature's geometry");
    7008             :             }
    7009             :         }
    7010          36 :         m_oClipDstEnv = OGREnvelope();
    7011             :     }
    7012             : 
    7013             :     const auto poGeom = m_poClipDstReprojectedToDstSRS
    7014          39 :                             ? m_poClipDstReprojectedToDstSRS.get()
    7015          39 :                             : m_poClipDstOri;
    7016          39 :     if (poGeom && !m_oClipDstEnv.IsInit())
    7017             :     {
    7018          39 :         poGeom->getEnvelope(&m_oClipDstEnv);
    7019             :     }
    7020          39 :     return std::make_pair(poGeom, poGeom ? &m_oClipDstEnv : nullptr);
    7021             : }
    7022             : 
    7023             : /************************************************************************/
    7024             : /*                LayerTranslator::GetSrcClipGeom()                     */
    7025             : /************************************************************************/
    7026             : 
    7027             : /** Returns the source clip geometry and its envelope
    7028             :  *
    7029             :  * @param poGeomSRS The SRS into which the source clip geometry should be
    7030             :  *                  expressed.
    7031             :  * @return the source clip geometry and its envelope, or (nullptr, nullptr)
    7032             :  */
    7033             : std::pair<const OGRGeometry *, const OGREnvelope *>
    7034          45 : LayerTranslator::GetSrcClipGeom(const OGRSpatialReference *poGeomSRS)
    7035             : {
    7036          45 :     if (m_poClipSrcReprojectedToSrcSRS_SRS != poGeomSRS)
    7037             :     {
    7038          42 :         auto poClipSrcSRS = m_poClipSrcOri->getSpatialReference();
    7039          42 :         if (poClipSrcSRS && poGeomSRS && !poClipSrcSRS->IsSame(poGeomSRS))
    7040             :         {
    7041             :             // Transform clip geom to geometry SRS
    7042           1 :             m_poClipSrcReprojectedToSrcSRS.reset(m_poClipSrcOri->clone());
    7043           1 :             if (m_poClipSrcReprojectedToSrcSRS->transformTo(poGeomSRS) !=
    7044             :                 OGRERR_NONE)
    7045             :             {
    7046           0 :                 return std::make_pair(nullptr, nullptr);
    7047             :             }
    7048           1 :             m_poClipSrcReprojectedToSrcSRS_SRS = poGeomSRS;
    7049             :         }
    7050          41 :         else if (!poClipSrcSRS && poGeomSRS)
    7051             :         {
    7052          41 :             if (!m_bWarnedClipSrcSRS)
    7053             :             {
    7054           3 :                 m_bWarnedClipSrcSRS = true;
    7055           3 :                 CPLError(CE_Warning, CPLE_AppDefined,
    7056             :                          "Clip source geometry has no attached SRS, "
    7057             :                          "but the feature's geometry has one. "
    7058             :                          "Assuming clip source geometry SRS is the "
    7059             :                          "same as the feature's geometry");
    7060             :             }
    7061             :         }
    7062          42 :         m_oClipSrcEnv = OGREnvelope();
    7063             :     }
    7064             : 
    7065             :     const auto poGeom = m_poClipSrcReprojectedToSrcSRS
    7066          45 :                             ? m_poClipSrcReprojectedToSrcSRS.get()
    7067          45 :                             : m_poClipSrcOri;
    7068          45 :     if (poGeom && !m_oClipSrcEnv.IsInit())
    7069             :     {
    7070          45 :         poGeom->getEnvelope(&m_oClipSrcEnv);
    7071             :     }
    7072          45 :     return std::make_pair(poGeom, poGeom ? &m_oClipSrcEnv : nullptr);
    7073             : }
    7074             : 
    7075             : /************************************************************************/
    7076             : /*                   GDALVectorTranslateOptionsGetParser()              */
    7077             : /************************************************************************/
    7078             : 
    7079         758 : static std::unique_ptr<GDALArgumentParser> GDALVectorTranslateOptionsGetParser(
    7080             :     GDALVectorTranslateOptions *psOptions,
    7081             :     GDALVectorTranslateOptionsForBinary *psOptionsForBinary, int nCountClipSrc,
    7082             :     int nCountClipDst)
    7083             : {
    7084             :     auto argParser = std::make_unique<GDALArgumentParser>(
    7085         758 :         "ogr2ogr", /* bForBinary=*/psOptionsForBinary != nullptr);
    7086             : 
    7087         758 :     argParser->add_description(
    7088         758 :         _("Converts simple features data between file formats."));
    7089             : 
    7090         758 :     argParser->add_epilog(
    7091         758 :         _("For more details, consult https://gdal.org/programs/ogr2ogr.html"));
    7092             : 
    7093         758 :     argParser->add_output_format_argument(psOptions->osFormat);
    7094             : 
    7095         758 :     argParser->add_dataset_creation_options_argument(psOptions->aosDSCO);
    7096             : 
    7097         758 :     argParser->add_layer_creation_options_argument(psOptions->aosLCO);
    7098             : 
    7099         758 :     argParser->add_usage_newline();
    7100             : 
    7101             :     {
    7102         758 :         auto &group = argParser->add_mutually_exclusive_group();
    7103         758 :         group.add_argument("-append")
    7104         758 :             .flag()
    7105          31 :             .action([psOptions](const std::string &)
    7106         758 :                     { psOptions->eAccessMode = ACCESS_APPEND; })
    7107         758 :             .help(_("Append to existing layer instead of creating new."));
    7108             : 
    7109         758 :         group.add_argument("-upsert")
    7110         758 :             .flag()
    7111             :             .action(
    7112           1 :                 [psOptions](const std::string &)
    7113             :                 {
    7114           1 :                     psOptions->eAccessMode = ACCESS_APPEND;
    7115           1 :                     psOptions->bUpsert = true;
    7116         758 :                 })
    7117             :             .help(_("Variant of -append where the UpsertFeature() operation is "
    7118         758 :                     "used to insert or update features."));
    7119             : 
    7120         758 :         group.add_argument("-overwrite")
    7121         758 :             .flag()
    7122          12 :             .action([psOptions](const std::string &)
    7123         758 :                     { psOptions->eAccessMode = ACCESS_OVERWRITE; })
    7124         758 :             .help(_("Delete the output layer and recreate it empty."));
    7125             :     }
    7126             : 
    7127         758 :     argParser->add_argument("-update")
    7128         758 :         .flag()
    7129             :         .action(
    7130          22 :             [psOptions](const std::string &)
    7131             :             {
    7132             :                 /* Don't reset -append or -overwrite */
    7133           8 :                 if (psOptions->eAccessMode != ACCESS_APPEND &&
    7134           7 :                     psOptions->eAccessMode != ACCESS_OVERWRITE)
    7135           7 :                     psOptions->eAccessMode = ACCESS_UPDATE;
    7136         758 :             })
    7137             :         .help(_("Open existing output datasource in update mode rather than "
    7138         758 :                 "trying to create a new one."));
    7139             : 
    7140         758 :     argParser->add_argument("-sql")
    7141        1516 :         .metavar("<statement>|@<filename>")
    7142             :         .action(
    7143          22 :             [psOptions](const std::string &s)
    7144             :             {
    7145          11 :                 GByte *pabyRet = nullptr;
    7146          14 :                 if (!s.empty() && s.front() == '@' &&
    7147           3 :                     VSIIngestFile(nullptr, s.c_str() + 1, &pabyRet, nullptr,
    7148             :                                   1024 * 1024))
    7149             :                 {
    7150           3 :                     GDALRemoveBOM(pabyRet);
    7151           3 :                     char *pszSQLStatement = reinterpret_cast<char *>(pabyRet);
    7152             :                     psOptions->osSQLStatement =
    7153           3 :                         GDALRemoveSQLComments(pszSQLStatement);
    7154           3 :                     VSIFree(pszSQLStatement);
    7155             :                 }
    7156             :                 else
    7157             :                 {
    7158           8 :                     psOptions->osSQLStatement = s;
    7159             :                 }
    7160         769 :             })
    7161         758 :         .help(_("SQL statement to execute."));
    7162             : 
    7163         758 :     argParser->add_argument("-dialect")
    7164        1516 :         .metavar("<dialect>")
    7165         758 :         .store_into(psOptions->osDialect)
    7166         758 :         .help(_("SQL dialect."));
    7167             : 
    7168         758 :     argParser->add_argument("-spat")
    7169        1516 :         .metavar("<xmin> <ymin> <xmax> <ymax>")
    7170         758 :         .nargs(4)
    7171         758 :         .scan<'g', double>()
    7172             :         .help(_("Spatial query extents, in the SRS of the source layer(s) (or "
    7173         758 :                 "the one specified with -spat_srs."));
    7174             : 
    7175         758 :     argParser->add_argument("-where")
    7176        1516 :         .metavar("<restricted_where>|@<filename>")
    7177             :         .action(
    7178          16 :             [psOptions](const std::string &s)
    7179             :             {
    7180           8 :                 GByte *pabyRet = nullptr;
    7181           9 :                 if (!s.empty() && s.front() == '@' &&
    7182           1 :                     VSIIngestFile(nullptr, s.c_str() + 1, &pabyRet, nullptr,
    7183             :                                   1024 * 1024))
    7184             :                 {
    7185           1 :                     GDALRemoveBOM(pabyRet);
    7186           1 :                     char *pszWHERE = reinterpret_cast<char *>(pabyRet);
    7187           1 :                     psOptions->osWHERE = pszWHERE;
    7188           1 :                     VSIFree(pszWHERE);
    7189             :                 }
    7190             :                 else
    7191             :                 {
    7192           7 :                     psOptions->osWHERE = s;
    7193             :                 }
    7194         766 :             })
    7195         758 :         .help(_("Attribute query (like SQL WHERE)."));
    7196             : 
    7197         758 :     argParser->add_argument("-select")
    7198        1516 :         .metavar("<field_list>")
    7199             :         .action(
    7200          34 :             [psOptions](const std::string &s)
    7201             :             {
    7202          17 :                 psOptions->bSelFieldsSet = true;
    7203             :                 psOptions->aosSelFields =
    7204          17 :                     CSLTokenizeStringComplex(s.c_str(), ",", TRUE, FALSE);
    7205         758 :             })
    7206             :         .help(_("Comma-delimited list of fields from input layer to copy to "
    7207         758 :                 "the new layer."));
    7208             : 
    7209         758 :     argParser->add_argument("-nln")
    7210        1516 :         .metavar("<name>")
    7211         758 :         .store_into(psOptions->osNewLayerName)
    7212         758 :         .help(_("Assign an alternate name to the new layer."));
    7213             : 
    7214         758 :     argParser->add_argument("-nlt")
    7215        1516 :         .metavar("<type>")
    7216         758 :         .append()
    7217             :         .action(
    7218         223 :             [psOptions](const std::string &osGeomNameIn)
    7219             :             {
    7220          49 :                 bool bIs3D = false;
    7221          98 :                 std::string osGeomName(osGeomNameIn);
    7222          98 :                 if (osGeomName.size() > 3 &&
    7223          49 :                     STARTS_WITH_CI(osGeomName.c_str() + osGeomName.size() - 3,
    7224             :                                    "25D"))
    7225             :                 {
    7226           1 :                     bIs3D = true;
    7227           1 :                     osGeomName.resize(osGeomName.size() - 3);
    7228             :                 }
    7229          96 :                 else if (osGeomName.size() > 1 &&
    7230          48 :                          STARTS_WITH_CI(
    7231             :                              osGeomName.c_str() + osGeomName.size() - 1, "Z"))
    7232             :                 {
    7233           0 :                     bIs3D = true;
    7234           0 :                     osGeomName.pop_back();
    7235             :                 }
    7236          49 :                 if (EQUAL(osGeomName.c_str(), "NONE"))
    7237             :                 {
    7238           1 :                     if (psOptions->eGType != GEOMTYPE_UNCHANGED)
    7239             :                     {
    7240             :                         throw std::invalid_argument(
    7241           0 :                             "Unsupported combination of -nlt arguments.");
    7242             :                     }
    7243           1 :                     psOptions->eGType = wkbNone;
    7244             :                 }
    7245          48 :                 else if (EQUAL(osGeomName.c_str(), "GEOMETRY"))
    7246             :                 {
    7247           4 :                     if (psOptions->eGType != GEOMTYPE_UNCHANGED)
    7248             :                     {
    7249             :                         throw std::invalid_argument(
    7250           0 :                             "Unsupported combination of -nlt arguments.");
    7251             :                     }
    7252           4 :                     psOptions->eGType = wkbUnknown;
    7253             :                 }
    7254          44 :                 else if (EQUAL(osGeomName.c_str(), "PROMOTE_TO_MULTI"))
    7255             :                 {
    7256           8 :                     if (psOptions->eGeomTypeConversion == GTC_CONVERT_TO_LINEAR)
    7257           2 :                         psOptions->eGeomTypeConversion =
    7258             :                             GTC_PROMOTE_TO_MULTI_AND_CONVERT_TO_LINEAR;
    7259           6 :                     else if (psOptions->eGeomTypeConversion == GTC_DEFAULT)
    7260           5 :                         psOptions->eGeomTypeConversion = GTC_PROMOTE_TO_MULTI;
    7261             :                     else
    7262             :                     {
    7263             :                         throw std::invalid_argument(
    7264           1 :                             "Unsupported combination of -nlt arguments.");
    7265             :                     }
    7266             :                 }
    7267          36 :                 else if (EQUAL(osGeomName.c_str(), "CONVERT_TO_LINEAR"))
    7268             :                 {
    7269          12 :                     if (psOptions->eGeomTypeConversion == GTC_PROMOTE_TO_MULTI)
    7270           2 :                         psOptions->eGeomTypeConversion =
    7271             :                             GTC_PROMOTE_TO_MULTI_AND_CONVERT_TO_LINEAR;
    7272          10 :                     else if (psOptions->eGeomTypeConversion == GTC_DEFAULT)
    7273           9 :                         psOptions->eGeomTypeConversion = GTC_CONVERT_TO_LINEAR;
    7274             :                     else
    7275             :                     {
    7276             :                         throw std::invalid_argument(
    7277           1 :                             "Unsupported combination of -nlt arguments.");
    7278             :                     }
    7279             :                 }
    7280          24 :                 else if (EQUAL(osGeomName.c_str(), "CONVERT_TO_CURVE"))
    7281             :                 {
    7282           7 :                     if (psOptions->eGeomTypeConversion == GTC_DEFAULT)
    7283           5 :                         psOptions->eGeomTypeConversion = GTC_CONVERT_TO_CURVE;
    7284             :                     else
    7285             :                     {
    7286             :                         throw std::invalid_argument(
    7287           2 :                             "Unsupported combination of -nlt arguments.");
    7288             :                     }
    7289             :                 }
    7290             :                 else
    7291             :                 {
    7292          17 :                     if (psOptions->eGType != GEOMTYPE_UNCHANGED)
    7293             :                     {
    7294             :                         throw std::invalid_argument(
    7295           3 :                             "Unsupported combination of -nlt arguments.");
    7296             :                     }
    7297          14 :                     psOptions->eGType = OGRFromOGCGeomType(osGeomName.c_str());
    7298          14 :                     if (psOptions->eGType == wkbUnknown)
    7299             :                     {
    7300             :                         throw std::invalid_argument(
    7301             :                             CPLSPrintf("-nlt %s: type not recognised.",
    7302           0 :                                        osGeomName.c_str()));
    7303             :                     }
    7304             :                 }
    7305          42 :                 if (psOptions->eGType != GEOMTYPE_UNCHANGED &&
    7306          23 :                     psOptions->eGType != wkbNone && bIs3D)
    7307           1 :                     psOptions->eGType = wkbSetZ(
    7308             :                         static_cast<OGRwkbGeometryType>(psOptions->eGType));
    7309         800 :             })
    7310         758 :         .help(_("Define the geometry type for the created layer."));
    7311             : 
    7312         758 :     argParser->add_argument("-s_srs")
    7313        1516 :         .metavar("<srs_def>")
    7314         758 :         .store_into(psOptions->osSourceSRSDef)
    7315         758 :         .help(_("Set/override source SRS."));
    7316             : 
    7317             :     {
    7318         758 :         auto &group = argParser->add_mutually_exclusive_group();
    7319         758 :         group.add_argument("-a_srs")
    7320        1516 :             .metavar("<srs_def>")
    7321             :             .action(
    7322         353 :                 [psOptions](const std::string &osOutputSRSDef)
    7323             :                 {
    7324         115 :                     psOptions->osOutputSRSDef = osOutputSRSDef;
    7325         230 :                     if (EQUAL(psOptions->osOutputSRSDef.c_str(), "NULL") ||
    7326         115 :                         EQUAL(psOptions->osOutputSRSDef.c_str(), "NONE"))
    7327             :                     {
    7328           4 :                         psOptions->osOutputSRSDef.clear();
    7329           4 :                         psOptions->bNullifyOutputSRS = true;
    7330             :                     }
    7331         758 :                 })
    7332         758 :             .help(_("Assign an output SRS, but without reprojecting."));
    7333             : 
    7334         758 :         group.add_argument("-t_srs")
    7335        1516 :             .metavar("<srs_def>")
    7336             :             .action(
    7337          68 :                 [psOptions](const std::string &osOutputSRSDef)
    7338             :                 {
    7339          34 :                     psOptions->osOutputSRSDef = osOutputSRSDef;
    7340          34 :                     psOptions->bTransform = true;
    7341         758 :                 })
    7342             :             .help(_("Reproject/transform to this SRS on output, and assign it "
    7343         758 :                     "as output SRS."));
    7344             :     }
    7345             : 
    7346             :     ///////////////////////////////////////////////////////////////////////
    7347         758 :     argParser->add_group("Field related options");
    7348             : 
    7349         758 :     argParser->add_argument("-addfields")
    7350         758 :         .flag()
    7351             :         .action(
    7352           2 :             [psOptions](const std::string &)
    7353             :             {
    7354           2 :                 psOptions->bAddMissingFields = true;
    7355           2 :                 psOptions->eAccessMode = ACCESS_APPEND;
    7356         758 :             })
    7357         758 :         .help(_("Same as append, but add also any new fields."));
    7358             : 
    7359         758 :     argParser->add_argument("-relaxedFieldNameMatch")
    7360         758 :         .flag()
    7361           1 :         .action([psOptions](const std::string &)
    7362         758 :                 { psOptions->bExactFieldNameMatch = false; })
    7363             :         .help(_("Do field name matching between source and existing target "
    7364         758 :                 "layer in a more relaxed way."));
    7365             : 
    7366         758 :     argParser->add_argument("-fieldTypeToString")
    7367        1516 :         .metavar("All|<type1>[,<type2>]...")
    7368             :         .action(
    7369           0 :             [psOptions](const std::string &s)
    7370             :             {
    7371             :                 psOptions->aosFieldTypesToString =
    7372           0 :                     CSLTokenizeStringComplex(s.c_str(), " ,", FALSE, FALSE);
    7373           0 :                 CSLConstList iter = psOptions->aosFieldTypesToString.List();
    7374           0 :                 while (*iter)
    7375             :                 {
    7376           0 :                     if (IsFieldType(*iter))
    7377             :                     {
    7378             :                         /* Do nothing */
    7379             :                     }
    7380           0 :                     else if (EQUAL(*iter, "All"))
    7381             :                     {
    7382           0 :                         psOptions->aosFieldTypesToString.Clear();
    7383           0 :                         psOptions->aosFieldTypesToString.AddString("All");
    7384           0 :                         break;
    7385             :                     }
    7386             :                     else
    7387             :                     {
    7388             :                         throw std::invalid_argument(CPLSPrintf(
    7389             :                             "Unhandled type for fieldTypeToString option : %s",
    7390           0 :                             *iter));
    7391             :                     }
    7392           0 :                     iter++;
    7393             :                 }
    7394         758 :             })
    7395             :         .help(_("Converts any field of the specified type to a field of type "
    7396         758 :                 "string in the destination layer."));
    7397             : 
    7398         758 :     argParser->add_argument("-mapFieldType")
    7399        1516 :         .metavar("<srctype>|All=<dsttype>[,<srctype2>=<dsttype2>]...")
    7400             :         .action(
    7401          12 :             [psOptions](const std::string &s)
    7402             :             {
    7403             :                 psOptions->aosMapFieldType =
    7404           4 :                     CSLTokenizeStringComplex(s.c_str(), " ,", FALSE, FALSE);
    7405           4 :                 CSLConstList iter = psOptions->aosMapFieldType.List();
    7406           8 :                 while (*iter)
    7407             :                 {
    7408           4 :                     char *pszKey = nullptr;
    7409           4 :                     const char *pszValue = CPLParseNameValue(*iter, &pszKey);
    7410           4 :                     if (pszKey && pszValue)
    7411             :                     {
    7412           8 :                         if (!((IsFieldType(pszKey) || EQUAL(pszKey, "All")) &&
    7413           4 :                               IsFieldType(pszValue)))
    7414             :                         {
    7415           0 :                             CPLFree(pszKey);
    7416             :                             throw std::invalid_argument(CPLSPrintf(
    7417           0 :                                 "Invalid value for -mapFieldType : %s", *iter));
    7418             :                         }
    7419             :                     }
    7420           4 :                     CPLFree(pszKey);
    7421           4 :                     iter++;
    7422             :                 }
    7423         762 :             })
    7424         758 :         .help(_("Converts any field of the specified type to another type."));
    7425             : 
    7426         758 :     argParser->add_argument("-fieldmap")
    7427        1516 :         .metavar("<field_1>[,<field_2>]...")
    7428             :         .action(
    7429           4 :             [psOptions](const std::string &s)
    7430             :             {
    7431             :                 psOptions->aosFieldMap =
    7432           2 :                     CSLTokenizeStringComplex(s.c_str(), ",", FALSE, FALSE);
    7433         758 :             })
    7434             :         .help(_("Specifies the list of field indexes to be copied from the "
    7435         758 :                 "source to the destination."));
    7436             : 
    7437         758 :     argParser->add_argument("-splitlistfields")
    7438         758 :         .store_into(psOptions->bSplitListFields)
    7439             :         .help(_("Split fields of type list type into as many fields of scalar "
    7440         758 :                 "type as necessary."));
    7441             : 
    7442         758 :     argParser->add_argument("-maxsubfields")
    7443        1516 :         .metavar("<n>")
    7444         758 :         .scan<'i', int>()
    7445             :         .action(
    7446           0 :             [psOptions](const std::string &s)
    7447             :             {
    7448           0 :                 const int nVal = atoi(s.c_str());
    7449           0 :                 if (nVal > 0)
    7450             :                 {
    7451           0 :                     psOptions->nMaxSplitListSubFields = nVal;
    7452             :                 }
    7453         758 :             })
    7454             :         .help(_("To be combined with -splitlistfields to limit the number of "
    7455         758 :                 "subfields created for each split field."));
    7456             : 
    7457         758 :     argParser->add_argument("-emptyStrAsNull")
    7458         758 :         .store_into(psOptions->bEmptyStrAsNull)
    7459         758 :         .help(_("Treat empty string values as null."));
    7460             : 
    7461         758 :     argParser->add_argument("-forceNullable")
    7462         758 :         .store_into(psOptions->bForceNullable)
    7463             :         .help(_("Do not propagate not-nullable constraints to target layer if "
    7464         758 :                 "they exist in source layer."));
    7465             : 
    7466         758 :     argParser->add_argument("-unsetFieldWidth")
    7467         758 :         .store_into(psOptions->bUnsetFieldWidth)
    7468         758 :         .help(_("Set field width and precision to 0."));
    7469             : 
    7470         758 :     argParser->add_argument("-unsetDefault")
    7471         758 :         .store_into(psOptions->bUnsetDefault)
    7472             :         .help(_("Do not propagate default field values to target layer if they "
    7473         758 :                 "exist in source layer."));
    7474             : 
    7475         758 :     argParser->add_argument("-resolveDomains")
    7476         758 :         .store_into(psOptions->bResolveDomains)
    7477             :         .help(_("Cause any selected field that is linked to a coded field "
    7478         758 :                 "domain will be accompanied by an additional field."));
    7479             : 
    7480         758 :     argParser->add_argument("-dateTimeTo")
    7481        1516 :         .metavar("UTC|UTC(+|-)<HH>|UTC(+|-)<HH>:<MM>")
    7482             :         .action(
    7483          33 :             [psOptions](const std::string &s)
    7484             :             {
    7485          13 :                 const char *pszFormat = s.c_str();
    7486          13 :                 if (EQUAL(pszFormat, "UTC"))
    7487             :                 {
    7488           1 :                     psOptions->nTZOffsetInSec = 0;
    7489             :                 }
    7490          12 :                 else if (STARTS_WITH_CI(pszFormat, "UTC") &&
    7491          11 :                          (strlen(pszFormat) == strlen("UTC+HH") ||
    7492           9 :                           strlen(pszFormat) == strlen("UTC+HH:MM")) &&
    7493           7 :                          (pszFormat[3] == '+' || pszFormat[3] == '-'))
    7494             :                 {
    7495           6 :                     const int nHour = atoi(pszFormat + strlen("UTC+"));
    7496           6 :                     if (nHour < 0 || nHour > 14)
    7497             :                     {
    7498           1 :                         throw std::invalid_argument("Invalid UTC hour offset.");
    7499             :                     }
    7500           5 :                     else if (strlen(pszFormat) == strlen("UTC+HH"))
    7501             :                     {
    7502           0 :                         psOptions->nTZOffsetInSec = nHour * 3600;
    7503           0 :                         if (pszFormat[3] == '-')
    7504           0 :                             psOptions->nTZOffsetInSec =
    7505           0 :                                 -psOptions->nTZOffsetInSec;
    7506             :                     }
    7507             :                     else  // if( strlen(pszFormat) == strlen("UTC+HH:MM") )
    7508             :                     {
    7509           5 :                         const int nMin = atoi(pszFormat + strlen("UTC+HH:"));
    7510           5 :                         if (nMin == 0 || nMin == 15 || nMin == 30 || nMin == 45)
    7511             :                         {
    7512           4 :                             psOptions->nTZOffsetInSec =
    7513           4 :                                 nHour * 3600 + nMin * 60;
    7514           4 :                             if (pszFormat[3] == '-')
    7515           3 :                                 psOptions->nTZOffsetInSec =
    7516           3 :                                     -psOptions->nTZOffsetInSec;
    7517             :                         }
    7518             :                     }
    7519             :                 }
    7520          12 :                 if (psOptions->nTZOffsetInSec == TZ_OFFSET_INVALID)
    7521             :                 {
    7522             :                     throw std::invalid_argument(
    7523             :                         "Value of -dateTimeTo should be UTC, UTC(+|-)HH or "
    7524           7 :                         "UTC(+|-)HH:MM with HH in [0,14] and MM=00,15,30,45");
    7525             :                 }
    7526         763 :             })
    7527             :         .help(_("Converts date time values from the timezone specified in the "
    7528         758 :                 "source value to the target timezone."));
    7529             : 
    7530         758 :     argParser->add_argument("-noNativeData")
    7531         758 :         .flag()
    7532           1 :         .action([psOptions](const std::string &)
    7533         758 :                 { psOptions->bNativeData = false; })
    7534         758 :         .help(_("Disable copying of native data."));
    7535             : 
    7536             :     ///////////////////////////////////////////////////////////////////////
    7537         758 :     argParser->add_group("Advanced geometry and SRS related options");
    7538             : 
    7539         758 :     argParser->add_argument("-dim")
    7540        1516 :         .metavar("layer_dim|2|XY|3|XYZ|XYM|XYZM")
    7541             :         .action(
    7542          24 :             [psOptions](const std::string &osDim)
    7543             :             {
    7544          12 :                 if (EQUAL(osDim.c_str(), "layer_dim"))
    7545           2 :                     psOptions->nCoordDim = COORD_DIM_LAYER_DIM;
    7546          18 :                 else if (EQUAL(osDim.c_str(), "XY") ||
    7547           8 :                          EQUAL(osDim.c_str(), "2"))
    7548           3 :                     psOptions->nCoordDim = 2;
    7549          12 :                 else if (EQUAL(osDim.c_str(), "XYZ") ||
    7550           5 :                          EQUAL(osDim.c_str(), "3"))
    7551           3 :                     psOptions->nCoordDim = 3;
    7552           4 :                 else if (EQUAL(osDim.c_str(), "XYM"))
    7553           2 :                     psOptions->nCoordDim = COORD_DIM_XYM;
    7554           2 :                 else if (EQUAL(osDim.c_str(), "XYZM"))
    7555           2 :                     psOptions->nCoordDim = 4;
    7556             :                 else
    7557             :                 {
    7558             :                     throw std::invalid_argument(CPLSPrintf(
    7559           0 :                         "-dim %s: value not handled.", osDim.c_str()));
    7560             :                 }
    7561         770 :             })
    7562         758 :         .help(_("Force the coordinate dimension."));
    7563             : 
    7564         758 :     argParser->add_argument("-s_coord_epoch")
    7565        1516 :         .metavar("<epoch>")
    7566         758 :         .store_into(psOptions->dfSourceCoordinateEpoch)
    7567         758 :         .help(_("Assign a coordinate epoch, linked with the source SRS."));
    7568             : 
    7569         758 :     argParser->add_argument("-a_coord_epoch")
    7570        1516 :         .metavar("<epoch>")
    7571         758 :         .store_into(psOptions->dfOutputCoordinateEpoch)
    7572             :         .help(_("Assign a coordinate epoch, linked with the output SRS when "
    7573         758 :                 "-a_srs is used."));
    7574             : 
    7575         758 :     argParser->add_argument("-t_coord_epoch")
    7576        1516 :         .metavar("<epoch>")
    7577         758 :         .store_into(psOptions->dfOutputCoordinateEpoch)
    7578             :         .help(_("Assign a coordinate epoch, linked with the output SRS when "
    7579         758 :                 "-t_srs is used."));
    7580             : 
    7581         758 :     argParser->add_argument("-ct")
    7582        1516 :         .metavar("<pipeline_def>")
    7583             :         .action(
    7584           6 :             [psOptions](const std::string &s)
    7585             :             {
    7586           3 :                 psOptions->osCTPipeline = s;
    7587           3 :                 psOptions->bTransform = true;
    7588         758 :             })
    7589             :         .help(_("Override the default transformation from the source to the "
    7590         758 :                 "target CRS."));
    7591             : 
    7592         758 :     argParser->add_argument("-spat_srs")
    7593        1516 :         .metavar("<srs_def>")
    7594         758 :         .store_into(psOptions->osSpatSRSDef)
    7595         758 :         .help(_("Override spatial filter SRS."));
    7596             : 
    7597         758 :     argParser->add_argument("-geomfield")
    7598        1516 :         .metavar("<name>")
    7599             :         .action(
    7600           2 :             [psOptions](const std::string &s)
    7601             :             {
    7602           1 :                 psOptions->osGeomField = s;
    7603           1 :                 psOptions->bGeomFieldSet = true;
    7604         758 :             })
    7605             :         .help(_("Name of the geometry field on which the spatial filter "
    7606         758 :                 "operates on."));
    7607             : 
    7608         758 :     argParser->add_argument("-segmentize")
    7609        1516 :         .metavar("<max_dist>")
    7610         758 :         .store_into(psOptions->dfGeomOpParam)
    7611           2 :         .action([psOptions](const std::string &)
    7612         758 :                 { psOptions->eGeomOp = GEOMOP_SEGMENTIZE; })
    7613         758 :         .help(_("Maximum distance between 2 nodes."));
    7614             : 
    7615         758 :     argParser->add_argument("-simplify")
    7616        1516 :         .metavar("<tolerance>")
    7617         758 :         .store_into(psOptions->dfGeomOpParam)
    7618           1 :         .action([psOptions](const std::string &)
    7619         758 :                 { psOptions->eGeomOp = GEOMOP_SIMPLIFY_PRESERVE_TOPOLOGY; })
    7620         758 :         .help(_("Distance tolerance for simplification."));
    7621             : 
    7622         758 :     argParser->add_argument("-makevalid")
    7623         758 :         .flag()
    7624             :         .action(
    7625          10 :             [psOptions](const std::string &)
    7626             :             {
    7627           5 :                 if (!OGRGeometryFactory::haveGEOS())
    7628             :                 {
    7629             :                     throw std::invalid_argument(
    7630           0 :                         "-makevalid only supported for builds against GEOS");
    7631             :                 }
    7632           5 :                 psOptions->bMakeValid = true;
    7633         763 :             })
    7634             :         .help(_("Fix geometries to be valid regarding the rules of the Simple "
    7635         758 :                 "Features specification."));
    7636             : 
    7637         758 :     argParser->add_argument("-skipinvalid")
    7638         758 :         .flag()
    7639             :         .action(
    7640           2 :             [psOptions](const std::string &)
    7641             :             {
    7642           1 :                 if (!OGRGeometryFactory::haveGEOS())
    7643             :                 {
    7644             :                     throw std::invalid_argument(
    7645           0 :                         "-skipinvalid only supported for builds against GEOS");
    7646             :                 }
    7647           1 :                 psOptions->bSkipInvalidGeom = true;
    7648         759 :             })
    7649             :         .help(_("Whether to skip features with invalid geometries regarding the"
    7650         758 :                 "rules of the Simple Features specification."));
    7651             : 
    7652         758 :     argParser->add_argument("-wrapdateline")
    7653         758 :         .store_into(psOptions->bWrapDateline)
    7654         758 :         .help(_("Split geometries crossing the dateline meridian."));
    7655             : 
    7656         758 :     argParser->add_argument("-datelineoffset")
    7657        1516 :         .metavar("<val_in_degree>")
    7658         758 :         .default_value(psOptions->dfDateLineOffset)
    7659         758 :         .store_into(psOptions->dfDateLineOffset)
    7660         758 :         .help(_("Offset from dateline in degrees."));
    7661             : 
    7662         758 :     argParser->add_argument("-clipsrc")
    7663         758 :         .nargs(nCountClipSrc)
    7664        1516 :         .metavar("[<xmin> <ymin> <xmax> <ymax>]|<WKT>|<datasource>|spat_extent")
    7665         758 :         .help(_("Clip geometries (in source SRS)."));
    7666             : 
    7667         758 :     argParser->add_argument("-clipsrcsql")
    7668        1516 :         .metavar("<sql_statement>")
    7669         758 :         .store_into(psOptions->osClipSrcSQL)
    7670             :         .help(_("Select desired geometries from the source clip datasource "
    7671         758 :                 "using an SQL query."));
    7672             : 
    7673         758 :     argParser->add_argument("-clipsrclayer")
    7674        1516 :         .metavar("<layername>")
    7675         758 :         .store_into(psOptions->osClipSrcLayer)
    7676         758 :         .help(_("Select the named layer from the source clip datasource."));
    7677             : 
    7678         758 :     argParser->add_argument("-clipsrcwhere")
    7679        1516 :         .metavar("<expression>")
    7680         758 :         .store_into(psOptions->osClipSrcWhere)
    7681             :         .help(_("Restrict desired geometries from the source clip layer based "
    7682         758 :                 "on an attribute query."));
    7683             : 
    7684         758 :     argParser->add_argument("-clipdst")
    7685         758 :         .nargs(nCountClipDst)
    7686        1516 :         .metavar("[<xmin> <ymin> <xmax> <ymax>]|<WKT>|<datasource>")
    7687         758 :         .help(_("Clip geometries (in target SRS)."));
    7688             : 
    7689         758 :     argParser->add_argument("-clipdstsql")
    7690        1516 :         .metavar("<sql_statement>")
    7691         758 :         .store_into(psOptions->osClipDstSQL)
    7692             :         .help(_("Select desired geometries from the destination clip "
    7693         758 :                 "datasource using an SQL query."));
    7694             : 
    7695         758 :     argParser->add_argument("-clipdstlayer")
    7696        1516 :         .metavar("<layername>")
    7697         758 :         .store_into(psOptions->osClipDstLayer)
    7698             :         .help(
    7699         758 :             _("Select the named layer from the destination clip datasource."));
    7700             : 
    7701         758 :     argParser->add_argument("-clipdstwhere")
    7702        1516 :         .metavar("<expression>")
    7703         758 :         .store_into(psOptions->osClipDstWhere)
    7704             :         .help(_("Restrict desired geometries from the destination clip layer "
    7705         758 :                 "based on an attribute query."));
    7706             : 
    7707         758 :     argParser->add_argument("-explodecollections")
    7708         758 :         .store_into(psOptions->bExplodeCollections)
    7709             :         .help(_("Produce one feature for each geometry in any kind of geometry "
    7710         758 :                 "collection in the source file."));
    7711             : 
    7712         758 :     argParser->add_argument("-zfield")
    7713        1516 :         .metavar("<name>")
    7714         758 :         .store_into(psOptions->osZField)
    7715             :         .help(_("Uses the specified field to fill the Z coordinate of "
    7716         758 :                 "geometries."));
    7717             : 
    7718         758 :     argParser->add_argument("-gcp")
    7719             :         .metavar(
    7720        1516 :             "<ungeoref_x> <ungeoref_y> <georef_x> <georef_y> [<elevation>]")
    7721         758 :         .nargs(4, 5)
    7722         758 :         .append()
    7723         758 :         .scan<'g', double>()
    7724         758 :         .help(_("Add the indicated ground control point."));
    7725             : 
    7726         758 :     argParser->add_argument("-tps")
    7727         758 :         .flag()
    7728           1 :         .action([psOptions](const std::string &)
    7729         758 :                 { psOptions->nTransformOrder = -1; })
    7730             :         .help(_("Force use of thin plate spline transformer based on available "
    7731         758 :                 "GCPs."));
    7732             : 
    7733         758 :     argParser->add_argument("-order")
    7734        1516 :         .metavar("1|2|3")
    7735         758 :         .store_into(psOptions->nTransformOrder)
    7736         758 :         .help(_("Order of polynomial used for warping."));
    7737             : 
    7738         758 :     argParser->add_argument("-xyRes")
    7739        1516 :         .metavar("<val>[ m|mm|deg]")
    7740             :         .action(
    7741          25 :             [psOptions](const std::string &s)
    7742             :             {
    7743           9 :                 const char *pszVal = s.c_str();
    7744             : 
    7745           9 :                 char *endptr = nullptr;
    7746           9 :                 psOptions->dfXYRes = CPLStrtodM(pszVal, &endptr);
    7747           9 :                 if (!endptr)
    7748             :                 {
    7749             :                     throw std::invalid_argument(
    7750             :                         "Invalid value for -xyRes. Must be of the form "
    7751           0 :                         "{numeric_value}[ ]?[m|mm|deg]?");
    7752             :                 }
    7753           9 :                 if (*endptr == ' ')
    7754           6 :                     ++endptr;
    7755           9 :                 if (*endptr != 0 && strcmp(endptr, "m") != 0 &&
    7756           5 :                     strcmp(endptr, "mm") != 0 && strcmp(endptr, "deg") != 0)
    7757             :                 {
    7758             :                     throw std::invalid_argument(
    7759             :                         "Invalid value for -xyRes. Must be of the form "
    7760           2 :                         "{numeric_value}[ ]?[m|mm|deg]?");
    7761             :                 }
    7762           7 :                 psOptions->osXYResUnit = endptr;
    7763         765 :             })
    7764         758 :         .help(_("Set/override the geometry X/Y coordinate resolution."));
    7765             : 
    7766         758 :     argParser->add_argument("-zRes")
    7767        1516 :         .metavar("<val>[ m|mm]")
    7768             :         .action(
    7769          16 :             [psOptions](const std::string &s)
    7770             :             {
    7771           6 :                 const char *pszVal = s.c_str();
    7772             : 
    7773           6 :                 char *endptr = nullptr;
    7774           6 :                 psOptions->dfZRes = CPLStrtodM(pszVal, &endptr);
    7775           6 :                 if (!endptr)
    7776             :                 {
    7777             :                     throw std::invalid_argument(
    7778             :                         "Invalid value for -zRes. Must be of the form "
    7779           0 :                         "{numeric_value}[ ]?[m|mm]?");
    7780             :                 }
    7781           6 :                 if (*endptr == ' ')
    7782           4 :                     ++endptr;
    7783           6 :                 if (*endptr != 0 && strcmp(endptr, "m") != 0 &&
    7784           3 :                     strcmp(endptr, "mm") != 0 && strcmp(endptr, "deg") != 0)
    7785             :                 {
    7786             :                     throw std::invalid_argument(
    7787             :                         "Invalid value for -zRes. Must be of the form "
    7788           2 :                         "{numeric_value}[ ]?[m|mm]?");
    7789             :                 }
    7790           4 :                 psOptions->osZResUnit = endptr;
    7791         762 :             })
    7792         758 :         .help(_("Set/override the geometry Z coordinate resolution."));
    7793             : 
    7794         758 :     argParser->add_argument("-mRes")
    7795        1516 :         .metavar("<val>")
    7796         758 :         .store_into(psOptions->dfMRes)
    7797         758 :         .help(_("Set/override the geometry M coordinate resolution."));
    7798             : 
    7799         758 :     argParser->add_argument("-unsetCoordPrecision")
    7800         758 :         .store_into(psOptions->bUnsetCoordPrecision)
    7801             :         .help(_("Prevent the geometry coordinate resolution from being set on "
    7802         758 :                 "target layer(s)."));
    7803             : 
    7804             :     ///////////////////////////////////////////////////////////////////////
    7805         758 :     argParser->add_group("Other options");
    7806             : 
    7807         758 :     argParser->add_quiet_argument(&psOptions->bQuiet);
    7808             : 
    7809         758 :     argParser->add_argument("-progress")
    7810         758 :         .store_into(psOptions->bDisplayProgress)
    7811             :         .help(_("Display progress on terminal. Only works if input layers have "
    7812         758 :                 "the 'fast feature count' capability."));
    7813             : 
    7814             :     argParser->add_input_format_argument(
    7815             :         psOptionsForBinary ? &psOptionsForBinary->aosAllowInputDrivers
    7816         758 :                            : nullptr);
    7817             : 
    7818             :     argParser->add_open_options_argument(
    7819         758 :         psOptionsForBinary ? &(psOptionsForBinary->aosOpenOptions) : nullptr);
    7820             : 
    7821         758 :     argParser->add_argument("-doo")
    7822        1516 :         .metavar("<NAME>=<VALUE>")
    7823         758 :         .append()
    7824           0 :         .action([psOptions](const std::string &s)
    7825         758 :                 { psOptions->aosDestOpenOptions.AddString(s.c_str()); })
    7826         758 :         .help(_("Open option(s) for output dataset."));
    7827             : 
    7828         758 :     argParser->add_usage_newline();
    7829             : 
    7830         758 :     argParser->add_argument("-fid")
    7831        1516 :         .metavar("<FID>")
    7832         758 :         .store_into(psOptions->nFIDToFetch)
    7833             :         .help(_("If provided, only the feature with the specified feature id "
    7834         758 :                 "will be processed."));
    7835             : 
    7836         758 :     argParser->add_argument("-preserve_fid")
    7837         758 :         .store_into(psOptions->bPreserveFID)
    7838             :         .help(_("Use the FID of the source features instead of letting the "
    7839         758 :                 "output driver automatically assign a new one."));
    7840             : 
    7841         758 :     argParser->add_argument("-unsetFid")
    7842         758 :         .store_into(psOptions->bUnsetFid)
    7843             :         .help(_("Prevent the name of the source FID column and source feature "
    7844         758 :                 "IDs from being re-used."));
    7845             : 
    7846             :     {
    7847         758 :         auto &group = argParser->add_mutually_exclusive_group();
    7848         758 :         group.add_argument("-skip", "-skipfailures")
    7849         758 :             .flag()
    7850             :             .action(
    7851           4 :                 [psOptions](const std::string &)
    7852             :                 {
    7853           4 :                     psOptions->bSkipFailures = true;
    7854           4 :                     psOptions->nGroupTransactions = 1; /* #2409 */
    7855         758 :                 })
    7856         758 :             .help(_("Continue after a failure, skipping the failed feature."));
    7857             : 
    7858         758 :         auto &arg = group.add_argument("-gt")
    7859        1516 :                         .metavar("<n>|unlimited")
    7860             :                         .action(
    7861           8 :                             [psOptions](const std::string &s)
    7862             :                             {
    7863             :                                 /* If skipfailures is already set we should not
    7864             :                modify nGroupTransactions = 1  #2409 */
    7865           4 :                                 if (!psOptions->bSkipFailures)
    7866             :                                 {
    7867           4 :                                     if (EQUAL(s.c_str(), "unlimited"))
    7868           1 :                                         psOptions->nGroupTransactions = -1;
    7869             :                                     else
    7870           3 :                                         psOptions->nGroupTransactions =
    7871           3 :                                             atoi(s.c_str());
    7872             :                                 }
    7873         758 :                             })
    7874         758 :                         .help(_("Group <n> features per transaction "));
    7875             : 
    7876         758 :         argParser->add_hidden_alias_for(arg, "tg");
    7877             :     }
    7878             : 
    7879         758 :     argParser->add_argument("-limit")
    7880        1516 :         .metavar("<nb_features>")
    7881         758 :         .store_into(psOptions->nLimit)
    7882         758 :         .help(_("Limit the number of features per layer."));
    7883             : 
    7884         758 :     argParser->add_argument("-ds_transaction")
    7885         758 :         .flag()
    7886             :         .action(
    7887           1 :             [psOptions](const std::string &)
    7888             :             {
    7889           1 :                 psOptions->nLayerTransaction = FALSE;
    7890           1 :                 psOptions->bForceTransaction = true;
    7891         758 :             })
    7892         758 :         .help(_("Force the use of a dataset level transaction."));
    7893             : 
    7894             :     /* Undocumented. Just a provision. Default behavior should be OK */
    7895         758 :     argParser->add_argument("-lyr_transaction")
    7896         758 :         .flag()
    7897         758 :         .hidden()
    7898           0 :         .action([psOptions](const std::string &)
    7899         758 :                 { psOptions->nLayerTransaction = TRUE; })
    7900         758 :         .help(_("Force the use of a layer level transaction."));
    7901             : 
    7902             :     argParser->add_metadata_item_options_argument(
    7903         758 :         psOptions->aosMetadataOptions);
    7904             : 
    7905         758 :     argParser->add_argument("-nomd")
    7906         758 :         .flag()
    7907           4 :         .action([psOptions](const std::string &)
    7908         758 :                 { psOptions->bCopyMD = false; })
    7909             :         .help(_("Disable copying of metadata from source dataset and layers "
    7910         758 :                 "into target dataset and layers."));
    7911             : 
    7912         758 :     if (psOptionsForBinary)
    7913             :     {
    7914         135 :         argParser->add_argument("dst_dataset_name")
    7915         270 :             .metavar("<dst_dataset_name>")
    7916         135 :             .store_into(psOptionsForBinary->osDestDataSource)
    7917         135 :             .help(_("Output dataset."));
    7918             : 
    7919         135 :         argParser->add_argument("src_dataset_name")
    7920         270 :             .metavar("<src_dataset_name>")
    7921         135 :             .store_into(psOptionsForBinary->osDataSource)
    7922         135 :             .help(_("Input dataset."));
    7923             :     }
    7924             : 
    7925         758 :     argParser->add_argument("layer")
    7926         758 :         .remaining()
    7927        1516 :         .metavar("<layer_name>")
    7928         758 :         .help(_("Layer name"));
    7929         758 :     return argParser;
    7930             : }
    7931             : 
    7932             : /************************************************************************/
    7933             : /*                    GDALVectorTranslateGetParserUsage()               */
    7934             : /************************************************************************/
    7935             : 
    7936           1 : std::string GDALVectorTranslateGetParserUsage()
    7937             : {
    7938             :     try
    7939             :     {
    7940           2 :         GDALVectorTranslateOptions sOptions;
    7941           2 :         GDALVectorTranslateOptionsForBinary sOptionsForBinary;
    7942             :         auto argParser = GDALVectorTranslateOptionsGetParser(
    7943           2 :             &sOptions, &sOptionsForBinary, 1, 1);
    7944           1 :         return argParser->usage();
    7945             :     }
    7946           0 :     catch (const std::exception &err)
    7947             :     {
    7948           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
    7949           0 :                  err.what());
    7950           0 :         return std::string();
    7951             :     }
    7952             : }
    7953             : 
    7954             : /************************************************************************/
    7955             : /*                   CHECK_HAS_ENOUGH_ADDITIONAL_ARGS()                 */
    7956             : /************************************************************************/
    7957             : 
    7958             : #ifndef CheckHasEnoughAdditionalArgs_defined
    7959             : #define CheckHasEnoughAdditionalArgs_defined
    7960             : 
    7961          51 : static bool CheckHasEnoughAdditionalArgs(CSLConstList papszArgv, int i,
    7962             :                                          int nExtraArg, int nArgc)
    7963             : {
    7964          51 :     if (i + nExtraArg >= nArgc)
    7965             :     {
    7966           2 :         CPLError(CE_Failure, CPLE_IllegalArg,
    7967           2 :                  "%s option requires %d argument%s", papszArgv[i], nExtraArg,
    7968             :                  nExtraArg == 1 ? "" : "s");
    7969           2 :         return false;
    7970             :     }
    7971          49 :     return true;
    7972             : }
    7973             : #endif
    7974             : 
    7975             : #define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg)                            \
    7976             :     if (!CheckHasEnoughAdditionalArgs(papszArgv, i, nExtraArg, nArgc))         \
    7977             :     {                                                                          \
    7978             :         return nullptr;                                                        \
    7979             :     }
    7980             : 
    7981             : /************************************************************************/
    7982             : /*                       GDALVectorTranslateOptionsNew()                */
    7983             : /************************************************************************/
    7984             : 
    7985             : /**
    7986             :  * allocates a GDALVectorTranslateOptions struct.
    7987             :  *
    7988             :  * @param papszArgv NULL terminated list of options (potentially including
    7989             :  * filename and open options too), or NULL. The accepted options are the ones of
    7990             :  * the <a href="/programs/ogr2ogr.html">ogr2ogr</a> utility.
    7991             :  * @param psOptionsForBinary (output) may be NULL (and should generally be
    7992             :  * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
    7993             :  *                           GDALVectorTranslateOptionsForBinaryNew() prior to
    7994             :  * this function. Will be filled with potentially present filename, open
    7995             :  * options,...
    7996             :  * @return pointer to the allocated GDALVectorTranslateOptions struct. Must be
    7997             :  * freed with GDALVectorTranslateOptionsFree().
    7998             :  *
    7999             :  * @since GDAL 2.1
    8000             :  */
    8001         761 : GDALVectorTranslateOptions *GDALVectorTranslateOptionsNew(
    8002             :     char **papszArgv, GDALVectorTranslateOptionsForBinary *psOptionsForBinary)
    8003             : {
    8004        1519 :     auto psOptions = std::make_unique<GDALVectorTranslateOptions>();
    8005             : 
    8006             :     /* -------------------------------------------------------------------- */
    8007             :     /*      Pre-processing for custom syntax that ArgumentParser does not   */
    8008             :     /*      support.                                                        */
    8009             :     /* -------------------------------------------------------------------- */
    8010             : 
    8011        1519 :     CPLStringList aosArgv;
    8012         761 :     const int nArgc = CSLCount(papszArgv);
    8013         761 :     int nCountClipSrc = 0;
    8014         761 :     int nCountClipDst = 0;
    8015        3605 :     for (int i = 0;
    8016        3605 :          i < nArgc && papszArgv != nullptr && papszArgv[i] != nullptr; i++)
    8017             :     {
    8018        2848 :         if (EQUAL(papszArgv[i], "-gcp"))
    8019             :         {
    8020             :             // repeated argument of varying size: not handled by argparse.
    8021             : 
    8022          18 :             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(4);
    8023          18 :             char *endptr = nullptr;
    8024             :             /* -gcp pixel line easting northing [elev] */
    8025             : 
    8026          18 :             psOptions->oGCPs.nGCPCount++;
    8027          36 :             psOptions->oGCPs.pasGCPs = static_cast<GDAL_GCP *>(
    8028          18 :                 CPLRealloc(psOptions->oGCPs.pasGCPs,
    8029          18 :                            sizeof(GDAL_GCP) * psOptions->oGCPs.nGCPCount));
    8030          18 :             GDALInitGCPs(1, psOptions->oGCPs.pasGCPs +
    8031          18 :                                 psOptions->oGCPs.nGCPCount - 1);
    8032             : 
    8033          18 :             psOptions->oGCPs.pasGCPs[psOptions->oGCPs.nGCPCount - 1]
    8034          18 :                 .dfGCPPixel = CPLAtof(papszArgv[++i]);
    8035          18 :             psOptions->oGCPs.pasGCPs[psOptions->oGCPs.nGCPCount - 1].dfGCPLine =
    8036          18 :                 CPLAtof(papszArgv[++i]);
    8037          18 :             psOptions->oGCPs.pasGCPs[psOptions->oGCPs.nGCPCount - 1].dfGCPX =
    8038          18 :                 CPLAtof(papszArgv[++i]);
    8039          18 :             psOptions->oGCPs.pasGCPs[psOptions->oGCPs.nGCPCount - 1].dfGCPY =
    8040          18 :                 CPLAtof(papszArgv[++i]);
    8041          33 :             if (papszArgv[i + 1] != nullptr &&
    8042          15 :                 (CPLStrtod(papszArgv[i + 1], &endptr) != 0.0 ||
    8043          15 :                  papszArgv[i + 1][0] == '0'))
    8044             :             {
    8045             :                 /* Check that last argument is really a number and not a
    8046             :                  * filename */
    8047             :                 /* looking like a number (see ticket #863) */
    8048           0 :                 if (endptr && *endptr == 0)
    8049           0 :                     psOptions->oGCPs.pasGCPs[psOptions->oGCPs.nGCPCount - 1]
    8050           0 :                         .dfGCPZ = CPLAtof(papszArgv[++i]);
    8051             :             }
    8052             : 
    8053             :             /* should set id and info? */
    8054             :         }
    8055             : 
    8056        2830 :         else if (EQUAL(papszArgv[i], "-clipsrc"))
    8057             :         {
    8058          18 :             if (nCountClipSrc)
    8059             :             {
    8060           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Duplicate argument %s",
    8061           1 :                          papszArgv[i]);
    8062           1 :                 return nullptr;
    8063             :             }
    8064             :             // argparse doesn't handle well variable number of values
    8065             :             // just before the positional arguments, so we have to detect
    8066             :             // it manually and set the correct number.
    8067          17 :             nCountClipSrc = 1;
    8068          17 :             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
    8069          19 :             if (CPLGetValueType(papszArgv[i + 1]) != CPL_VALUE_STRING &&
    8070           3 :                 i + 4 < nArgc)
    8071             :             {
    8072           2 :                 nCountClipSrc = 4;
    8073             :             }
    8074             : 
    8075          54 :             for (int j = 0; j < 1 + nCountClipSrc; ++j)
    8076             :             {
    8077          38 :                 aosArgv.AddString(papszArgv[i]);
    8078          38 :                 ++i;
    8079             :             }
    8080          16 :             --i;
    8081             :         }
    8082             : 
    8083        2812 :         else if (EQUAL(papszArgv[i], "-clipdst"))
    8084             :         {
    8085          17 :             if (nCountClipDst)
    8086             :             {
    8087           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Duplicate argument %s",
    8088           1 :                          papszArgv[i]);
    8089           1 :                 return nullptr;
    8090             :             }
    8091             :             // argparse doesn't handle well variable number of values
    8092             :             // just before the positional arguments, so we have to detect
    8093             :             // it manually and set the correct number.
    8094          16 :             nCountClipDst = 1;
    8095          16 :             CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
    8096          19 :             if (CPLGetValueType(papszArgv[i + 1]) != CPL_VALUE_STRING &&
    8097           4 :                 i + 4 < nArgc)
    8098             :             {
    8099           3 :                 nCountClipDst = 4;
    8100             :             }
    8101             : 
    8102          54 :             for (int j = 0; j < 1 + nCountClipDst; ++j)
    8103             :             {
    8104          39 :                 aosArgv.AddString(papszArgv[i]);
    8105          39 :                 ++i;
    8106             :             }
    8107          15 :             --i;
    8108             :         }
    8109             : 
    8110             :         else
    8111             :         {
    8112        2795 :             aosArgv.AddString(papszArgv[i]);
    8113             :         }
    8114             :     }
    8115             : 
    8116             :     try
    8117             :     {
    8118             :         auto argParser = GDALVectorTranslateOptionsGetParser(
    8119        1511 :             psOptions.get(), psOptionsForBinary, nCountClipSrc, nCountClipDst);
    8120             : 
    8121             :         // Collect non-positional arguments for VectorTranslateFrom() case
    8122         756 :         psOptions->aosArguments =
    8123        1513 :             argParser->get_non_positional_arguments(aosArgv);
    8124             : 
    8125         756 :         argParser->parse_args_without_binary_name(aosArgv.List());
    8126             : 
    8127         730 :         if (psOptionsForBinary)
    8128         130 :             psOptionsForBinary->bQuiet = psOptions->bQuiet;
    8129             : 
    8130         738 :         if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
    8131             :         {
    8132          16 :             OGRLinearRing oRing;
    8133           8 :             const double dfMinX = (*oSpat)[0];
    8134           8 :             const double dfMinY = (*oSpat)[1];
    8135           8 :             const double dfMaxX = (*oSpat)[2];
    8136           8 :             const double dfMaxY = (*oSpat)[3];
    8137             : 
    8138           8 :             oRing.addPoint(dfMinX, dfMinY);
    8139           8 :             oRing.addPoint(dfMinX, dfMaxY);
    8140           8 :             oRing.addPoint(dfMaxX, dfMaxY);
    8141           8 :             oRing.addPoint(dfMaxX, dfMinY);
    8142           8 :             oRing.addPoint(dfMinX, dfMinY);
    8143             : 
    8144          16 :             auto poSpatialFilter = std::make_shared<OGRPolygon>();
    8145           8 :             poSpatialFilter->addRing(&oRing);
    8146           8 :             psOptions->poSpatialFilter = poSpatialFilter;
    8147             :         }
    8148             : 
    8149         730 :         if (auto oClipSrc =
    8150         730 :                 argParser->present<std::vector<std::string>>("-clipsrc"))
    8151             :         {
    8152          15 :             const std::string &osVal = (*oClipSrc)[0];
    8153             : 
    8154          15 :             psOptions->poClipSrc.reset();
    8155          15 :             psOptions->osClipSrcDS.clear();
    8156             : 
    8157             :             VSIStatBufL sStat;
    8158          15 :             psOptions->bClipSrc = true;
    8159          15 :             if (oClipSrc->size() == 4)
    8160             :             {
    8161           1 :                 const double dfMinX = CPLAtofM((*oClipSrc)[0].c_str());
    8162           1 :                 const double dfMinY = CPLAtofM((*oClipSrc)[1].c_str());
    8163           1 :                 const double dfMaxX = CPLAtofM((*oClipSrc)[2].c_str());
    8164           1 :                 const double dfMaxY = CPLAtofM((*oClipSrc)[3].c_str());
    8165             : 
    8166           2 :                 OGRLinearRing oRing;
    8167             : 
    8168           1 :                 oRing.addPoint(dfMinX, dfMinY);
    8169           1 :                 oRing.addPoint(dfMinX, dfMaxY);
    8170           1 :                 oRing.addPoint(dfMaxX, dfMaxY);
    8171           1 :                 oRing.addPoint(dfMaxX, dfMinY);
    8172           1 :                 oRing.addPoint(dfMinX, dfMinY);
    8173             : 
    8174           2 :                 auto poPoly = std::make_shared<OGRPolygon>();
    8175           1 :                 psOptions->poClipSrc = poPoly;
    8176           1 :                 poPoly->addRing(&oRing);
    8177             :             }
    8178          14 :             else if ((STARTS_WITH_CI(osVal.c_str(), "POLYGON") ||
    8179          18 :                       STARTS_WITH_CI(osVal.c_str(), "MULTIPOLYGON")) &&
    8180           4 :                      VSIStatL(osVal.c_str(), &sStat) != 0)
    8181             :             {
    8182           4 :                 OGRGeometry *poGeom = nullptr;
    8183           4 :                 OGRGeometryFactory::createFromWkt(osVal.c_str(), nullptr,
    8184             :                                                   &poGeom);
    8185           4 :                 psOptions->poClipSrc.reset(poGeom);
    8186           4 :                 if (psOptions->poClipSrc == nullptr)
    8187             :                 {
    8188           0 :                     CPLError(
    8189             :                         CE_Failure, CPLE_IllegalArg,
    8190             :                         "Invalid -clipsrc geometry. Must be a valid POLYGON or "
    8191             :                         "MULTIPOLYGON WKT");
    8192           0 :                     return nullptr;
    8193             :                 }
    8194             :             }
    8195          10 :             else if (EQUAL(osVal.c_str(), "spat_extent"))
    8196             :             {
    8197             :                 // Nothing to do
    8198             :             }
    8199             :             else
    8200             :             {
    8201           9 :                 psOptions->osClipSrcDS = osVal;
    8202             :             }
    8203             :         }
    8204             : 
    8205         730 :         if (auto oClipDst =
    8206         730 :                 argParser->present<std::vector<std::string>>("-clipdst"))
    8207             :         {
    8208          14 :             const std::string &osVal = (*oClipDst)[0];
    8209             : 
    8210          14 :             psOptions->poClipDst.reset();
    8211          14 :             psOptions->osClipDstDS.clear();
    8212             : 
    8213             :             VSIStatBufL sStat;
    8214          14 :             if (oClipDst->size() == 4)
    8215             :             {
    8216           2 :                 const double dfMinX = CPLAtofM((*oClipDst)[0].c_str());
    8217           2 :                 const double dfMinY = CPLAtofM((*oClipDst)[1].c_str());
    8218           2 :                 const double dfMaxX = CPLAtofM((*oClipDst)[2].c_str());
    8219           2 :                 const double dfMaxY = CPLAtofM((*oClipDst)[3].c_str());
    8220             : 
    8221           4 :                 OGRLinearRing oRing;
    8222             : 
    8223           2 :                 oRing.addPoint(dfMinX, dfMinY);
    8224           2 :                 oRing.addPoint(dfMinX, dfMaxY);
    8225           2 :                 oRing.addPoint(dfMaxX, dfMaxY);
    8226           2 :                 oRing.addPoint(dfMaxX, dfMinY);
    8227           2 :                 oRing.addPoint(dfMinX, dfMinY);
    8228             : 
    8229           4 :                 auto poPoly = std::make_shared<OGRPolygon>();
    8230           2 :                 psOptions->poClipDst = poPoly;
    8231           2 :                 poPoly->addRing(&oRing);
    8232             :             }
    8233          12 :             else if ((STARTS_WITH_CI(osVal.c_str(), "POLYGON") ||
    8234          15 :                       STARTS_WITH_CI(osVal.c_str(), "MULTIPOLYGON")) &&
    8235           3 :                      VSIStatL(osVal.c_str(), &sStat) != 0)
    8236             :             {
    8237           3 :                 OGRGeometry *poGeom = nullptr;
    8238           3 :                 OGRGeometryFactory::createFromWkt(osVal.c_str(), nullptr,
    8239             :                                                   &poGeom);
    8240           3 :                 psOptions->poClipDst.reset(poGeom);
    8241           3 :                 if (psOptions->poClipDst == nullptr)
    8242             :                 {
    8243           0 :                     CPLError(
    8244             :                         CE_Failure, CPLE_IllegalArg,
    8245             :                         "Invalid -clipdst geometry. Must be a valid POLYGON or "
    8246             :                         "MULTIPOLYGON WKT");
    8247           0 :                     return nullptr;
    8248             :                 }
    8249             :             }
    8250             :             else
    8251             :             {
    8252           9 :                 psOptions->osClipDstDS = osVal;
    8253             :             }
    8254             :         }
    8255             : 
    8256        1460 :         auto layers = argParser->present<std::vector<std::string>>("layer");
    8257         730 :         if (layers)
    8258             :         {
    8259          49 :             for (const auto &layer : *layers)
    8260             :             {
    8261          34 :                 psOptions->aosLayers.AddString(layer.c_str());
    8262             :             }
    8263             :         }
    8264         730 :         if (psOptionsForBinary)
    8265             :         {
    8266         130 :             psOptionsForBinary->eAccessMode = psOptions->eAccessMode;
    8267         130 :             psOptionsForBinary->osFormat = psOptions->osFormat;
    8268             : 
    8269         130 :             if (!(CPLTestBool(
    8270             :                     psOptionsForBinary->aosOpenOptions.FetchNameValueDef(
    8271             :                         "NATIVE_DATA",
    8272             :                         psOptionsForBinary->aosOpenOptions.FetchNameValueDef(
    8273             :                             "@NATIVE_DATA", "TRUE")))))
    8274             :             {
    8275           0 :                 psOptions->bNativeData = false;
    8276             :             }
    8277             : 
    8278         130 :             if (psOptions->bNativeData &&
    8279         129 :                 psOptionsForBinary->aosOpenOptions.FetchNameValue(
    8280         259 :                     "NATIVE_DATA") == nullptr &&
    8281         129 :                 psOptionsForBinary->aosOpenOptions.FetchNameValue(
    8282             :                     "@NATIVE_DATA") == nullptr)
    8283             :             {
    8284             :                 psOptionsForBinary->aosOpenOptions.AddString(
    8285         129 :                     "@NATIVE_DATA=YES");
    8286             :             }
    8287             :         }
    8288             : 
    8289         730 :         return psOptions.release();
    8290             :     }
    8291          24 :     catch (const std::exception &err)
    8292             :     {
    8293          24 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
    8294          24 :         if (psOptionsForBinary)
    8295           1 :             psOptionsForBinary->bShowUsageIfError = true;
    8296          24 :         return nullptr;
    8297             :     }
    8298             : }
    8299             : 
    8300             : /************************************************************************/
    8301             : /*                      GDALVectorTranslateOptionsFree()                */
    8302             : /************************************************************************/
    8303             : 
    8304             : /**
    8305             :  * Frees the GDALVectorTranslateOptions struct.
    8306             :  *
    8307             :  * @param psOptions the options struct for GDALVectorTranslate().
    8308             :  * @since GDAL 2.1
    8309             :  */
    8310             : 
    8311         730 : void GDALVectorTranslateOptionsFree(GDALVectorTranslateOptions *psOptions)
    8312             : {
    8313         730 :     delete psOptions;
    8314         730 : }
    8315             : 
    8316             : /************************************************************************/
    8317             : /*                 GDALVectorTranslateOptionsSetProgress()              */
    8318             : /************************************************************************/
    8319             : 
    8320             : /**
    8321             :  * Set a progress function.
    8322             :  *
    8323             :  * @param psOptions the options struct for GDALVectorTranslate().
    8324             :  * @param pfnProgress the progress callback.
    8325             :  * @param pProgressData the user data for the progress callback.
    8326             :  *
    8327             :  * @since GDAL 2.1
    8328             :  */
    8329             : 
    8330         131 : void GDALVectorTranslateOptionsSetProgress(
    8331             :     GDALVectorTranslateOptions *psOptions, GDALProgressFunc pfnProgress,
    8332             :     void *pProgressData)
    8333             : {
    8334         131 :     psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
    8335         131 :     psOptions->pProgressData = pProgressData;
    8336         131 :     if (pfnProgress == GDALTermProgress)
    8337         129 :         psOptions->bQuiet = false;
    8338         131 : }
    8339             : 
    8340             : #undef CHECK_HAS_ENOUGH_ADDITIONAL_ARGS

Generated by: LCOV version 1.14