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