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