LCOV - code coverage report
Current view: top level - apps - ogr2ogr_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3215 3990 80.6 %
Date: 2025-05-15 13:16:46 Functions: 115 149 77.2 %

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

Generated by: LCOV version 1.14