LCOV - code coverage report
Current view: top level - apps - ogr2ogr_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2885 3604 80.0 %
Date: 2024-05-03 15:49:35 Functions: 104 134 77.6 %

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

Generated by: LCOV version 1.14