LCOV - code coverage report
Current view: top level - apps - ogr2ogr_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3254 4035 80.6 %
Date: 2026-03-19 04:40:40 Functions: 114 151 75.5 %

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

Generated by: LCOV version 1.14