LCOV - code coverage report
Current view: top level - apps - ogr2ogr_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3226 3999 80.7 %
Date: 2025-10-15 23:46:56 Functions: 115 150 76.7 %

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

Generated by: LCOV version 1.14