Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Simple client for viewing OGR driver data.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "cpl_json.h"
16 : #include "ogrlibjsonutils.h"
17 : #include "cpl_string.h"
18 : #include "gdal_utils.h"
19 : #include "gdal_utils_priv.h"
20 : #include "gdal_priv.h"
21 : #include "ogr_feature.h"
22 : #include "ogrsf_frmts.h"
23 : #include "ogr_geometry.h"
24 : #include "commonutils.h"
25 : #include "gdalargumentparser.h"
26 :
27 : #include <cmath>
28 : #include <set>
29 :
30 : /*! output format */
31 : typedef enum
32 : {
33 : /*! output in text format */ FORMAT_TEXT = 0,
34 : /*! output in json format */ FORMAT_JSON = 1
35 : } GDALVectorInfoFormat;
36 :
37 : struct GDALVectorInfoOptions
38 : {
39 : GDALVectorInfoFormat eFormat = FORMAT_TEXT;
40 : std::string osWHERE{};
41 : CPLStringList aosLayers{};
42 : std::unique_ptr<OGRGeometry> poSpatialFilter{};
43 : bool bAllLayers = false;
44 : std::string osSQLStatement{};
45 : std::string osDialect{};
46 : std::string osGeomField{};
47 : CPLStringList aosExtraMDDomains{};
48 : bool bListMDD = false;
49 : bool bShowMetadata = true;
50 : bool bFeatureCount = true;
51 : bool bExtent = true;
52 : bool bExtent3D = false;
53 : bool bGeomType = true;
54 : bool bDatasetGetNextFeature = false;
55 : bool bVerbose = true;
56 : bool bSuperQuiet = false;
57 : bool bSummaryOnly = false;
58 : GIntBig nFetchFID = OGRNullFID;
59 : std::string osWKTFormat = "WKT2";
60 : std::string osFieldDomain{};
61 : CPLStringList aosOptions{};
62 : bool bStdoutOutput = false; // only set by ogrinfo_bin
63 : int nRepeatCount = 1;
64 :
65 : /*! Maximum number of features, or -1 if no limit. */
66 : GIntBig nLimit = -1;
67 :
68 : // Only used during argument parsing
69 : bool bSummaryUserRequested = false;
70 : bool bFeaturesUserRequested = false;
71 :
72 : // Set by gdal vector info
73 : bool bIsCli = false;
74 :
75 : // Select the OGR_SCHEMA export
76 : bool bExportOgrSchema = false;
77 : };
78 :
79 : /************************************************************************/
80 : /* GDALVectorInfoOptionsFree() */
81 : /************************************************************************/
82 :
83 : /**
84 : * Frees the GDALVectorInfoOptions struct.
85 : *
86 : * @param psOptions the options struct for GDALVectorInfo().
87 : *
88 : * @since GDAL 3.7
89 : */
90 :
91 121 : void GDALVectorInfoOptionsFree(GDALVectorInfoOptions *psOptions)
92 : {
93 121 : delete psOptions;
94 121 : }
95 :
96 : /************************************************************************/
97 : /* Concat() */
98 : /************************************************************************/
99 :
100 : #ifndef Concat_defined
101 : #define Concat_defined
102 : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
103 : ...) CPL_PRINT_FUNC_FORMAT(3, 4);
104 :
105 2885 : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
106 : ...)
107 : {
108 : va_list args;
109 2885 : va_start(args, pszFormat);
110 :
111 2885 : if (bStdoutOutput)
112 : {
113 2234 : vfprintf(stdout, pszFormat, args);
114 : }
115 : else
116 : {
117 : try
118 : {
119 1302 : CPLString osTarget;
120 651 : osTarget.vPrintf(pszFormat, args);
121 :
122 651 : osRet += osTarget;
123 : }
124 0 : catch (const std::bad_alloc &)
125 : {
126 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
127 : }
128 : }
129 :
130 2885 : va_end(args);
131 2885 : }
132 : #endif
133 :
134 758 : static void ConcatStr(CPLString &osRet, bool bStdoutOutput, const char *pszStr)
135 : {
136 758 : if (bStdoutOutput)
137 616 : fwrite(pszStr, 1, strlen(pszStr), stdout);
138 : else
139 142 : osRet += pszStr;
140 758 : }
141 :
142 : /************************************************************************/
143 : /* ReportFieldDomain() */
144 : /************************************************************************/
145 :
146 14 : static void ReportFieldDomain(CPLString &osRet, CPLJSONObject &oDomains,
147 : const GDALVectorInfoOptions *psOptions,
148 : const OGRFieldDomain *poDomain)
149 : {
150 14 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
151 28 : CPLJSONObject oDomain;
152 14 : oDomains.Add(poDomain->GetName(), oDomain);
153 14 : Concat(osRet, psOptions->bStdoutOutput, "Domain %s:\n",
154 14 : poDomain->GetName().c_str());
155 14 : const std::string &osDesc = poDomain->GetDescription();
156 14 : if (!osDesc.empty())
157 : {
158 2 : if (bJson)
159 1 : oDomain.Set("description", osDesc);
160 : else
161 1 : Concat(osRet, psOptions->bStdoutOutput, " Description: %s\n",
162 : osDesc.c_str());
163 : }
164 14 : const char *pszType = "";
165 14 : CPL_IGNORE_RET_VAL(pszType); // Make CSA happy
166 14 : switch (poDomain->GetDomainType())
167 : {
168 2 : case OFDT_CODED:
169 2 : pszType = "coded";
170 2 : break;
171 10 : case OFDT_RANGE:
172 10 : pszType = "range";
173 10 : break;
174 2 : case OFDT_GLOB:
175 2 : pszType = "glob";
176 2 : break;
177 : }
178 14 : if (bJson)
179 : {
180 7 : oDomain.Set("type", pszType);
181 : }
182 : else
183 : {
184 7 : Concat(osRet, psOptions->bStdoutOutput, " Type: %s\n", pszType);
185 : }
186 : const char *pszFieldType =
187 14 : OGRFieldDefn::GetFieldTypeName(poDomain->GetFieldType());
188 : const char *pszFieldSubType =
189 14 : OGRFieldDefn::GetFieldSubTypeName(poDomain->GetFieldSubType());
190 14 : if (bJson)
191 : {
192 7 : oDomain.Set("fieldType", pszFieldType);
193 7 : if (poDomain->GetFieldSubType() != OFSTNone)
194 0 : oDomain.Set("fieldSubType", pszFieldSubType);
195 : }
196 : else
197 : {
198 : const char *pszFieldTypeDisplay =
199 7 : (poDomain->GetFieldSubType() != OFSTNone)
200 7 : ? CPLSPrintf("%s(%s)", pszFieldType, pszFieldSubType)
201 7 : : pszFieldType;
202 7 : Concat(osRet, psOptions->bStdoutOutput, " Field type: %s\n",
203 : pszFieldTypeDisplay);
204 : }
205 :
206 14 : const char *pszSplitPolicy = "";
207 14 : CPL_IGNORE_RET_VAL(pszSplitPolicy); // Make CSA happy
208 14 : switch (poDomain->GetSplitPolicy())
209 : {
210 14 : case OFDSP_DEFAULT_VALUE:
211 14 : pszSplitPolicy = "default value";
212 14 : break;
213 0 : case OFDSP_DUPLICATE:
214 0 : pszSplitPolicy = "duplicate";
215 0 : break;
216 0 : case OFDSP_GEOMETRY_RATIO:
217 0 : pszSplitPolicy = "geometry ratio";
218 0 : break;
219 : }
220 14 : if (bJson)
221 : {
222 7 : oDomain.Set("splitPolicy", pszSplitPolicy);
223 : }
224 : else
225 : {
226 7 : Concat(osRet, psOptions->bStdoutOutput, " Split policy: %s\n",
227 : pszSplitPolicy);
228 : }
229 :
230 14 : const char *pszMergePolicy = "";
231 14 : CPL_IGNORE_RET_VAL(pszMergePolicy); // Make CSA happy
232 14 : switch (poDomain->GetMergePolicy())
233 : {
234 14 : case OFDMP_DEFAULT_VALUE:
235 14 : pszMergePolicy = "default value";
236 14 : break;
237 0 : case OFDMP_SUM:
238 0 : pszMergePolicy = "sum";
239 0 : break;
240 0 : case OFDMP_GEOMETRY_WEIGHTED:
241 0 : pszMergePolicy = "geometry weighted";
242 0 : break;
243 : }
244 14 : if (bJson)
245 : {
246 7 : oDomain.Set("mergePolicy", pszMergePolicy);
247 : }
248 : else
249 : {
250 7 : Concat(osRet, psOptions->bStdoutOutput, " Merge policy: %s\n",
251 : pszMergePolicy);
252 : }
253 :
254 14 : switch (poDomain->GetDomainType())
255 : {
256 2 : case OFDT_CODED:
257 : {
258 : const auto poCodedFieldDomain =
259 2 : cpl::down_cast<const OGRCodedFieldDomain *>(poDomain);
260 : const OGRCodedValue *enumeration =
261 2 : poCodedFieldDomain->GetEnumeration();
262 2 : if (!bJson)
263 1 : Concat(osRet, psOptions->bStdoutOutput, " Coded values:\n");
264 4 : CPLJSONObject oCodedValues;
265 2 : oDomain.Add("codedValues", oCodedValues);
266 6 : for (int i = 0; enumeration[i].pszCode != nullptr; ++i)
267 : {
268 4 : if (enumeration[i].pszValue)
269 : {
270 2 : if (bJson)
271 : {
272 1 : oCodedValues.Set(enumeration[i].pszCode,
273 1 : enumeration[i].pszValue);
274 : }
275 : else
276 : {
277 1 : Concat(osRet, psOptions->bStdoutOutput, " %s: %s\n",
278 1 : enumeration[i].pszCode, enumeration[i].pszValue);
279 : }
280 : }
281 : else
282 : {
283 2 : if (bJson)
284 : {
285 1 : oCodedValues.SetNull(enumeration[i].pszCode);
286 : }
287 : else
288 : {
289 1 : Concat(osRet, psOptions->bStdoutOutput, " %s\n",
290 1 : enumeration[i].pszCode);
291 : }
292 : }
293 : }
294 2 : break;
295 : }
296 :
297 10 : case OFDT_RANGE:
298 : {
299 : const auto poRangeFieldDomain =
300 10 : cpl::down_cast<const OGRRangeFieldDomain *>(poDomain);
301 10 : bool bMinIsIncluded = false;
302 10 : const OGRField &sMin = poRangeFieldDomain->GetMin(bMinIsIncluded);
303 10 : bool bMaxIsIncluded = false;
304 10 : const OGRField &sMax = poRangeFieldDomain->GetMax(bMaxIsIncluded);
305 10 : if (poDomain->GetFieldType() == OFTInteger)
306 : {
307 2 : if (!OGR_RawField_IsUnset(&sMin))
308 : {
309 2 : if (bJson)
310 : {
311 1 : oDomain.Set("minValue", sMin.Integer);
312 1 : oDomain.Set("minValueIncluded", bMinIsIncluded);
313 : }
314 : else
315 : {
316 1 : Concat(osRet, psOptions->bStdoutOutput,
317 1 : " Minimum value: %d%s\n", sMin.Integer,
318 : bMinIsIncluded ? "" : " (excluded)");
319 : }
320 : }
321 2 : if (!OGR_RawField_IsUnset(&sMax))
322 : {
323 2 : if (bJson)
324 : {
325 1 : oDomain.Set("maxValue", sMax.Integer);
326 1 : oDomain.Set("maxValueIncluded", bMaxIsIncluded);
327 : }
328 : else
329 : {
330 1 : Concat(osRet, psOptions->bStdoutOutput,
331 1 : " Maximum value: %d%s\n", sMax.Integer,
332 : bMaxIsIncluded ? "" : " (excluded)");
333 : }
334 : }
335 : }
336 8 : else if (poDomain->GetFieldType() == OFTInteger64)
337 : {
338 2 : if (!OGR_RawField_IsUnset(&sMin))
339 : {
340 2 : if (bJson)
341 : {
342 1 : oDomain.Set("minValue", sMin.Integer64);
343 1 : oDomain.Set("minValueIncluded", bMinIsIncluded);
344 : }
345 : else
346 : {
347 1 : Concat(osRet, psOptions->bStdoutOutput,
348 : " Minimum value: " CPL_FRMT_GIB "%s\n",
349 1 : sMin.Integer64,
350 : bMinIsIncluded ? "" : " (excluded)");
351 : }
352 : }
353 2 : if (!OGR_RawField_IsUnset(&sMax))
354 : {
355 2 : if (bJson)
356 : {
357 1 : oDomain.Set("maxValue", sMax.Integer64);
358 1 : oDomain.Set("maxValueIncluded", bMaxIsIncluded);
359 : }
360 : else
361 : {
362 1 : Concat(osRet, psOptions->bStdoutOutput,
363 : " Maximum value: " CPL_FRMT_GIB "%s\n",
364 1 : sMax.Integer64,
365 : bMaxIsIncluded ? "" : " (excluded)");
366 : }
367 : }
368 : }
369 6 : else if (poDomain->GetFieldType() == OFTReal)
370 : {
371 4 : if (!OGR_RawField_IsUnset(&sMin))
372 : {
373 2 : if (bJson)
374 : {
375 1 : oDomain.Set("minValue", sMin.Real);
376 1 : oDomain.Set("minValueIncluded", bMinIsIncluded);
377 : }
378 : else
379 : {
380 1 : Concat(osRet, psOptions->bStdoutOutput,
381 1 : " Minimum value: %g%s\n", sMin.Real,
382 : bMinIsIncluded ? "" : " (excluded)");
383 : }
384 : }
385 4 : if (!OGR_RawField_IsUnset(&sMax))
386 : {
387 2 : if (bJson)
388 : {
389 1 : oDomain.Set("maxValue", sMax.Real);
390 1 : oDomain.Set("maxValueIncluded", bMaxIsIncluded);
391 : }
392 : else
393 : {
394 1 : Concat(osRet, psOptions->bStdoutOutput,
395 1 : " Maximum value: %g%s\n", sMax.Real,
396 : bMaxIsIncluded ? "" : " (excluded)");
397 : }
398 : }
399 : }
400 2 : else if (poDomain->GetFieldType() == OFTDateTime)
401 : {
402 2 : if (!OGR_RawField_IsUnset(&sMin))
403 : {
404 4 : const char *pszVal = CPLSPrintf(
405 2 : "%04d-%02d-%02dT%02d:%02d:%02d", sMin.Date.Year,
406 2 : sMin.Date.Month, sMin.Date.Day, sMin.Date.Hour,
407 2 : sMin.Date.Minute,
408 2 : static_cast<int>(sMin.Date.Second + 0.5f));
409 2 : if (bJson)
410 : {
411 1 : oDomain.Set("minValue", pszVal);
412 1 : oDomain.Set("minValueIncluded", bMinIsIncluded);
413 : }
414 : else
415 : {
416 1 : Concat(osRet, psOptions->bStdoutOutput,
417 : " Minimum value: %s%s\n", pszVal,
418 : bMinIsIncluded ? "" : " (excluded)");
419 : }
420 : }
421 2 : if (!OGR_RawField_IsUnset(&sMax))
422 : {
423 4 : const char *pszVal = CPLSPrintf(
424 2 : "%04d-%02d-%02dT%02d:%02d:%02d", sMax.Date.Year,
425 2 : sMax.Date.Month, sMax.Date.Day, sMax.Date.Hour,
426 2 : sMax.Date.Minute,
427 2 : static_cast<int>(sMax.Date.Second + 0.5f));
428 2 : if (bJson)
429 : {
430 1 : oDomain.Set("maxValue", pszVal);
431 1 : oDomain.Set("maxValueIncluded", bMaxIsIncluded);
432 : }
433 : else
434 : {
435 1 : Concat(osRet, psOptions->bStdoutOutput,
436 : " Maximum value: %s%s\n", pszVal,
437 : bMaxIsIncluded ? "" : " (excluded)");
438 : }
439 : }
440 : }
441 10 : break;
442 : }
443 :
444 2 : case OFDT_GLOB:
445 : {
446 : const auto poGlobFieldDomain =
447 2 : cpl::down_cast<const OGRGlobFieldDomain *>(poDomain);
448 2 : if (bJson)
449 1 : oDomain.Set("glob", poGlobFieldDomain->GetGlob());
450 : else
451 1 : Concat(osRet, psOptions->bStdoutOutput, " Glob: %s\n",
452 1 : poGlobFieldDomain->GetGlob().c_str());
453 2 : break;
454 : }
455 : }
456 14 : }
457 :
458 : /************************************************************************/
459 : /* ReportRelationships() */
460 : /************************************************************************/
461 :
462 91 : static void ReportRelationships(CPLString &osRet, CPLJSONObject &oRoot,
463 : const GDALVectorInfoOptions *psOptions,
464 : const GDALDataset *poDS)
465 : {
466 91 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
467 182 : CPLJSONObject oRelationships;
468 91 : if (bJson)
469 38 : oRoot.Add("relationships", oRelationships);
470 :
471 182 : const auto aosRelationshipNames = poDS->GetRelationshipNames();
472 113 : for (const std::string &osRelationshipName : aosRelationshipNames)
473 : {
474 22 : const auto poRelationship = poDS->GetRelationship(osRelationshipName);
475 22 : if (!poRelationship)
476 0 : continue;
477 :
478 22 : const char *pszType = "";
479 22 : CPL_IGNORE_RET_VAL(pszType); // Make CSA happy
480 22 : switch (poRelationship->GetType())
481 : {
482 8 : case GRT_COMPOSITE:
483 8 : pszType = "Composite";
484 8 : break;
485 14 : case GRT_ASSOCIATION:
486 14 : pszType = "Association";
487 14 : break;
488 0 : case GRT_AGGREGATION:
489 0 : pszType = "Aggregation";
490 0 : break;
491 : }
492 :
493 22 : const char *pszCardinality = "";
494 22 : CPL_IGNORE_RET_VAL(pszCardinality); // Make CSA happy
495 22 : switch (poRelationship->GetCardinality())
496 : {
497 12 : case GRC_ONE_TO_ONE:
498 12 : pszCardinality = "OneToOne";
499 12 : break;
500 6 : case GRC_ONE_TO_MANY:
501 6 : pszCardinality = "OneToMany";
502 6 : break;
503 0 : case GRC_MANY_TO_ONE:
504 0 : pszCardinality = "ManyToOne";
505 0 : break;
506 4 : case GRC_MANY_TO_MANY:
507 4 : pszCardinality = "ManyToMany";
508 4 : break;
509 : }
510 :
511 22 : const auto &aosLeftTableFields = poRelationship->GetLeftTableFields();
512 22 : const auto &aosRightTableFields = poRelationship->GetRightTableFields();
513 22 : const auto &osMappingTableName = poRelationship->GetMappingTableName();
514 : const auto &aosLeftMappingTableFields =
515 22 : poRelationship->GetLeftMappingTableFields();
516 : const auto &aosRightMappingTableFields =
517 22 : poRelationship->GetRightMappingTableFields();
518 :
519 22 : if (bJson)
520 : {
521 22 : CPLJSONObject oRelationship;
522 11 : oRelationships.Add(osRelationshipName, oRelationship);
523 :
524 11 : oRelationship.Add("type", pszType);
525 11 : oRelationship.Add("related_table_type",
526 : poRelationship->GetRelatedTableType());
527 11 : oRelationship.Add("cardinality", pszCardinality);
528 11 : oRelationship.Add("left_table_name",
529 : poRelationship->GetLeftTableName());
530 11 : oRelationship.Add("right_table_name",
531 : poRelationship->GetRightTableName());
532 :
533 22 : CPLJSONArray oLeftTableFields;
534 11 : oRelationship.Add("left_table_fields", oLeftTableFields);
535 22 : for (const auto &osName : aosLeftTableFields)
536 11 : oLeftTableFields.Add(osName);
537 :
538 11 : CPLJSONArray oRightTableFields;
539 11 : oRelationship.Add("right_table_fields", oRightTableFields);
540 23 : for (const auto &osName : aosRightTableFields)
541 12 : oRightTableFields.Add(osName);
542 :
543 11 : if (!osMappingTableName.empty())
544 : {
545 2 : oRelationship.Add("mapping_table_name", osMappingTableName);
546 :
547 4 : CPLJSONArray oLeftMappingTableFields;
548 2 : oRelationship.Add("left_mapping_table_fields",
549 : oLeftMappingTableFields);
550 4 : for (const auto &osName : aosLeftMappingTableFields)
551 2 : oLeftMappingTableFields.Add(osName);
552 :
553 4 : CPLJSONArray oRightMappingTableFields;
554 2 : oRelationship.Add("right_mapping_table_fields",
555 : oRightMappingTableFields);
556 4 : for (const auto &osName : aosRightMappingTableFields)
557 2 : oRightMappingTableFields.Add(osName);
558 : }
559 :
560 11 : oRelationship.Add("forward_path_label",
561 : poRelationship->GetForwardPathLabel());
562 11 : oRelationship.Add("backward_path_label",
563 : poRelationship->GetBackwardPathLabel());
564 : }
565 : else
566 : {
567 : const auto ConcatStringList =
568 80 : [&osRet, psOptions](const std::vector<std::string> &aosList)
569 : {
570 26 : bool bFirstName = true;
571 53 : for (const auto &osName : aosList)
572 : {
573 27 : if (!bFirstName)
574 1 : ConcatStr(osRet, psOptions->bStdoutOutput, ", ");
575 27 : bFirstName = false;
576 27 : ConcatStr(osRet, psOptions->bStdoutOutput, osName.c_str());
577 : }
578 26 : Concat(osRet, psOptions->bStdoutOutput, "\n");
579 26 : };
580 :
581 11 : if (!psOptions->bAllLayers)
582 : {
583 0 : Concat(osRet, psOptions->bStdoutOutput,
584 : "Relationship: %s (%s, %s, %s)\n",
585 : osRelationshipName.c_str(), pszType,
586 0 : poRelationship->GetLeftTableName().c_str(),
587 0 : poRelationship->GetRightTableName().c_str());
588 0 : continue;
589 : }
590 11 : Concat(osRet, psOptions->bStdoutOutput, "\nRelationship: %s\n",
591 : osRelationshipName.c_str());
592 11 : Concat(osRet, psOptions->bStdoutOutput, " Type: %s\n", pszType);
593 11 : Concat(osRet, psOptions->bStdoutOutput,
594 : " Related table type: %s\n",
595 11 : poRelationship->GetRelatedTableType().c_str());
596 11 : Concat(osRet, psOptions->bStdoutOutput, " Cardinality: %s\n",
597 : pszCardinality);
598 11 : Concat(osRet, psOptions->bStdoutOutput, " Left table name: %s\n",
599 11 : poRelationship->GetLeftTableName().c_str());
600 11 : Concat(osRet, psOptions->bStdoutOutput, " Right table name: %s\n",
601 11 : poRelationship->GetRightTableName().c_str());
602 11 : Concat(osRet, psOptions->bStdoutOutput, " Left table fields: ");
603 11 : ConcatStringList(aosLeftTableFields);
604 11 : Concat(osRet, psOptions->bStdoutOutput, " Right table fields: ");
605 11 : ConcatStringList(aosRightTableFields);
606 :
607 11 : if (!osMappingTableName.empty())
608 : {
609 2 : Concat(osRet, psOptions->bStdoutOutput,
610 : " Mapping table name: %s\n",
611 : osMappingTableName.c_str());
612 :
613 2 : Concat(osRet, psOptions->bStdoutOutput,
614 : " Left mapping table fields: ");
615 2 : ConcatStringList(aosLeftMappingTableFields);
616 :
617 2 : Concat(osRet, psOptions->bStdoutOutput,
618 : " Right mapping table fields: ");
619 2 : ConcatStringList(aosRightMappingTableFields);
620 : }
621 :
622 11 : Concat(osRet, psOptions->bStdoutOutput,
623 : " Forward path label: %s\n",
624 11 : poRelationship->GetForwardPathLabel().c_str());
625 11 : Concat(osRet, psOptions->bStdoutOutput,
626 : " Backward path label: %s\n",
627 11 : poRelationship->GetBackwardPathLabel().c_str());
628 : }
629 : }
630 91 : }
631 :
632 : /************************************************************************/
633 : /* GDALVectorInfoPrintMetadata() */
634 : /************************************************************************/
635 :
636 : static void
637 560 : GDALVectorInfoPrintMetadata(CPLString &osRet, CPLJSONObject &oMetadata,
638 : const GDALVectorInfoOptions *psOptions,
639 : GDALMajorObjectH hObject, const char *pszDomain,
640 : const char *pszDisplayedname, const char *pszIndent)
641 : {
642 560 : const bool bJsonOutput = psOptions->eFormat == FORMAT_JSON;
643 560 : bool bIsxml = false;
644 560 : bool bMDIsJson = false;
645 :
646 560 : if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "xml:"))
647 0 : bIsxml = true;
648 560 : else if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "json:"))
649 1 : bMDIsJson = true;
650 :
651 560 : CSLConstList papszMetadata = GDALGetMetadata(hObject, pszDomain);
652 560 : if (CSLCount(papszMetadata) > 0)
653 : {
654 54 : CPLJSONObject oMetadataDomain;
655 54 : if (!bJsonOutput)
656 20 : Concat(osRet, psOptions->bStdoutOutput, "%s%s:\n", pszIndent,
657 : pszDisplayedname);
658 125 : for (int i = 0; papszMetadata[i] != nullptr; i++)
659 : {
660 72 : if (bJsonOutput)
661 : {
662 52 : if (bIsxml)
663 : {
664 0 : oMetadata.Add(pszDomain, papszMetadata[i]);
665 0 : return;
666 : }
667 52 : else if (bMDIsJson)
668 : {
669 1 : CPLJSONDocument oDoc;
670 1 : if (oDoc.LoadMemory(papszMetadata[i]))
671 1 : oMetadata.Add(pszDomain, oDoc.GetRoot());
672 1 : return;
673 : }
674 : else
675 : {
676 51 : char *pszKey = nullptr;
677 : const char *pszValue =
678 51 : CPLParseNameValue(papszMetadata[i], &pszKey);
679 51 : if (pszKey)
680 : {
681 51 : oMetadataDomain.Add(pszKey, pszValue);
682 51 : CPLFree(pszKey);
683 : }
684 : }
685 : }
686 20 : else if (bIsxml)
687 0 : Concat(osRet, psOptions->bStdoutOutput, "%s%s\n", pszIndent,
688 0 : papszMetadata[i]);
689 : else
690 20 : Concat(osRet, psOptions->bStdoutOutput, "%s %s\n", pszIndent,
691 20 : papszMetadata[i]);
692 : }
693 53 : if (bJsonOutput)
694 : {
695 33 : oMetadata.Add(pszDomain ? pszDomain : "", oMetadataDomain);
696 : }
697 : }
698 : }
699 :
700 : /************************************************************************/
701 : /* GDALVectorInfoReportMetadata() */
702 : /************************************************************************/
703 :
704 289 : static void GDALVectorInfoReportMetadata(CPLString &osRet, CPLJSONObject &oRoot,
705 : const GDALVectorInfoOptions *psOptions,
706 : GDALMajorObject *poMajorObject,
707 : bool bListMDD, bool bShowMetadata,
708 : CSLConstList papszExtraMDDomains)
709 : {
710 289 : const char *pszIndent = "";
711 289 : auto hObject = GDALMajorObject::ToHandle(poMajorObject);
712 :
713 289 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
714 : /* -------------------------------------------------------------------- */
715 : /* Report list of Metadata domains */
716 : /* -------------------------------------------------------------------- */
717 289 : if (bListMDD)
718 : {
719 0 : const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
720 :
721 0 : CPLJSONArray metadataDomains;
722 :
723 0 : if (!aosMDDList.empty() && !bJson)
724 0 : Concat(osRet, psOptions->bStdoutOutput, "%sMetadata domains:\n",
725 : pszIndent);
726 0 : for (const char *pszDomain : aosMDDList)
727 : {
728 0 : if (EQUAL(pszDomain, ""))
729 : {
730 0 : if (bJson)
731 0 : metadataDomains.Add("");
732 : else
733 0 : Concat(osRet, psOptions->bStdoutOutput, "%s (default)\n",
734 : pszIndent);
735 : }
736 : else
737 : {
738 0 : if (bJson)
739 0 : metadataDomains.Add(pszDomain);
740 : else
741 0 : Concat(osRet, psOptions->bStdoutOutput, "%s %s\n",
742 : pszIndent, pszDomain);
743 : }
744 : }
745 :
746 0 : if (bJson)
747 0 : oRoot.Add("metadataDomains", metadataDomains);
748 : }
749 :
750 289 : if (!bShowMetadata)
751 21 : return;
752 :
753 : /* -------------------------------------------------------------------- */
754 : /* Report default Metadata domain. */
755 : /* -------------------------------------------------------------------- */
756 536 : CPLJSONObject oMetadata;
757 268 : oRoot.Add("metadata", oMetadata);
758 268 : GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject, nullptr,
759 : "Metadata", pszIndent);
760 :
761 : /* -------------------------------------------------------------------- */
762 : /* Report extra Metadata domains */
763 : /* -------------------------------------------------------------------- */
764 268 : if (papszExtraMDDomains != nullptr)
765 : {
766 196 : CPLStringList aosExtraMDDomainsExpanded;
767 :
768 98 : if (EQUAL(papszExtraMDDomains[0], "all") &&
769 98 : papszExtraMDDomains[1] == nullptr)
770 : {
771 196 : const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
772 136 : for (const char *pszDomain : aosMDDList)
773 : {
774 38 : if (!EQUAL(pszDomain, "") && !EQUAL(pszDomain, "SUBDATASETS"))
775 : {
776 24 : aosExtraMDDomainsExpanded.AddString(pszDomain);
777 : }
778 98 : }
779 : }
780 : else
781 : {
782 0 : aosExtraMDDomainsExpanded = CSLDuplicate(papszExtraMDDomains);
783 : }
784 :
785 122 : for (const char *pszDomain : aosExtraMDDomainsExpanded)
786 : {
787 : const std::string osDisplayedName =
788 72 : std::string("Metadata (").append(pszDomain).append(")");
789 24 : GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject,
790 : pszDomain, osDisplayedName.c_str(),
791 : pszIndent);
792 : }
793 : }
794 268 : GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject,
795 : "SUBDATASETS", "Subdatasets", pszIndent);
796 : }
797 :
798 : /************************************************************************/
799 : /* ReportOnLayer() */
800 : /************************************************************************/
801 :
802 186 : static void ReportOnLayer(CPLString &osRet, CPLJSONObject &oLayer,
803 : const GDALVectorInfoOptions *psOptions,
804 : OGRLayer *poLayer, bool bForceSummary,
805 : bool bTakeIntoAccountWHERE,
806 : bool bTakeIntoAccountSpatialFilter,
807 : bool bTakeIntoAccountGeomField)
808 : {
809 186 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
810 186 : const bool bIsSummaryCli =
811 186 : psOptions->bIsCli && psOptions->bSummaryUserRequested;
812 186 : const bool bExportOgrSchema = psOptions->bExportOgrSchema;
813 186 : OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
814 :
815 186 : oLayer.Set("name", poLayer->GetName());
816 186 : if (bExportOgrSchema)
817 : {
818 19 : oLayer.Set("schemaType", "Full");
819 : }
820 : const int nGeomFieldCount =
821 186 : psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
822 :
823 : /* -------------------------------------------------------------------- */
824 : /* Set filters if provided. */
825 : /* -------------------------------------------------------------------- */
826 186 : if (bTakeIntoAccountWHERE && !psOptions->osWHERE.empty())
827 : {
828 4 : if (poLayer->SetAttributeFilter(psOptions->osWHERE.c_str()) !=
829 : OGRERR_NONE)
830 : {
831 0 : CPLError(CE_Failure, CPLE_AppDefined,
832 : "SetAttributeFilter(%s) failed.",
833 0 : psOptions->osWHERE.c_str());
834 0 : return;
835 : }
836 : }
837 :
838 186 : if (bTakeIntoAccountSpatialFilter && psOptions->poSpatialFilter != nullptr)
839 : {
840 2 : if (bTakeIntoAccountGeomField && !psOptions->osGeomField.empty())
841 : {
842 : const int iGeomField =
843 1 : poDefn->GetGeomFieldIndex(psOptions->osGeomField.c_str());
844 1 : if (iGeomField >= 0)
845 1 : poLayer->SetSpatialFilter(iGeomField,
846 1 : psOptions->poSpatialFilter.get());
847 : else
848 0 : CPLError(CE_Warning, CPLE_AppDefined,
849 : "Cannot find geometry field %s.",
850 0 : psOptions->osGeomField.c_str());
851 : }
852 : else
853 : {
854 1 : poLayer->SetSpatialFilter(psOptions->poSpatialFilter.get());
855 : }
856 : }
857 :
858 : /* -------------------------------------------------------------------- */
859 : /* Report various overall information. */
860 : /* -------------------------------------------------------------------- */
861 186 : if (!bJson && !psOptions->bSuperQuiet)
862 : {
863 113 : Concat(osRet, psOptions->bStdoutOutput, "\n");
864 113 : Concat(osRet, psOptions->bStdoutOutput, "Layer name: %s\n",
865 113 : poLayer->GetName());
866 : }
867 :
868 186 : GDALVectorInfoReportMetadata(osRet, oLayer, psOptions, poLayer,
869 186 : !bIsSummaryCli && psOptions->bListMDD,
870 186 : !bIsSummaryCli && psOptions->bShowMetadata,
871 186 : psOptions->aosExtraMDDomains.List());
872 :
873 186 : if (psOptions->bVerbose)
874 : {
875 :
876 370 : CPLString osWKTFormat("FORMAT=");
877 185 : osWKTFormat += psOptions->osWKTFormat;
878 185 : const char *const apszWKTOptions[] = {osWKTFormat.c_str(),
879 185 : "MULTILINE=YES", nullptr};
880 :
881 185 : if (bJson || nGeomFieldCount > 1)
882 : {
883 150 : CPLJSONArray oGeometryFields;
884 75 : if (bJson)
885 73 : oLayer.Add("geometryFields", oGeometryFields);
886 138 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
887 : {
888 : const OGRGeomFieldDefn *poGFldDefn =
889 63 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
890 63 : if (bJson)
891 : {
892 118 : CPLJSONObject oGeometryField;
893 59 : oGeometryFields.Add(oGeometryField);
894 59 : oGeometryField.Set("name", poGFldDefn->GetNameRef());
895 59 : oGeometryField.Set(
896 59 : "type", OGRToOGCGeomType(poGFldDefn->GetType(),
897 : /*bCamelCase=*/true,
898 : /*bAddZm=*/true,
899 : /*bSpaceBeforeZM=*/false));
900 59 : oGeometryField.Set("nullable",
901 59 : CPL_TO_BOOL(poGFldDefn->IsNullable()));
902 59 : if (psOptions->bExtent3D)
903 : {
904 3 : OGREnvelope3D oExt;
905 3 : if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) ==
906 : OGRERR_NONE)
907 : {
908 : {
909 3 : CPLJSONArray oBbox;
910 3 : oBbox.Add(oExt.MinX);
911 3 : oBbox.Add(oExt.MinY);
912 3 : oBbox.Add(oExt.MaxX);
913 3 : oBbox.Add(oExt.MaxY);
914 3 : oGeometryField.Add("extent", oBbox);
915 : }
916 : {
917 3 : CPLJSONArray oBbox;
918 3 : oBbox.Add(oExt.MinX);
919 3 : oBbox.Add(oExt.MinY);
920 3 : if (std::isfinite(oExt.MinZ))
921 2 : oBbox.Add(oExt.MinZ);
922 : else
923 1 : oBbox.AddNull();
924 3 : oBbox.Add(oExt.MaxX);
925 3 : oBbox.Add(oExt.MaxY);
926 3 : if (std::isfinite(oExt.MaxZ))
927 2 : oBbox.Add(oExt.MaxZ);
928 : else
929 1 : oBbox.AddNull();
930 3 : oGeometryField.Add("extent3D", oBbox);
931 : }
932 : }
933 : }
934 56 : else if (psOptions->bExtent)
935 : {
936 38 : OGREnvelope oExt;
937 38 : if (poLayer->GetExtent(iGeom, &oExt, TRUE) ==
938 : OGRERR_NONE)
939 : {
940 28 : CPLJSONArray oBbox;
941 28 : oBbox.Add(oExt.MinX);
942 28 : oBbox.Add(oExt.MinY);
943 28 : oBbox.Add(oExt.MaxX);
944 28 : oBbox.Add(oExt.MaxY);
945 28 : oGeometryField.Add("extent", oBbox);
946 : }
947 : }
948 : const OGRSpatialReference *poSRS =
949 59 : poGFldDefn->GetSpatialRef();
950 59 : if (poSRS)
951 : {
952 92 : CPLJSONObject oCRS;
953 46 : oGeometryField.Add("coordinateSystem", oCRS);
954 46 : char *pszWKT = nullptr;
955 46 : poSRS->exportToWkt(&pszWKT, apszWKTOptions);
956 46 : if (pszWKT)
957 : {
958 46 : oCRS.Set("wkt", pszWKT);
959 46 : CPLFree(pszWKT);
960 : }
961 :
962 : {
963 46 : char *pszProjJson = nullptr;
964 : // PROJJSON requires PROJ >= 6.2
965 : CPLErrorStateBackuper oCPLErrorHandlerPusher(
966 92 : CPLQuietErrorHandler);
967 46 : CPL_IGNORE_RET_VAL(
968 46 : poSRS->exportToPROJJSON(&pszProjJson, nullptr));
969 46 : if (pszProjJson)
970 : {
971 92 : CPLJSONDocument oDoc;
972 46 : if (oDoc.LoadMemory(pszProjJson))
973 : {
974 46 : oCRS.Add("projjson", oDoc.GetRoot());
975 : }
976 46 : CPLFree(pszProjJson);
977 : }
978 : }
979 :
980 : const auto &anAxes =
981 46 : poSRS->GetDataAxisToSRSAxisMapping();
982 92 : CPLJSONArray oAxisMapping;
983 139 : for (const auto nAxis : anAxes)
984 : {
985 93 : oAxisMapping.Add(nAxis);
986 : }
987 46 : oCRS.Add("dataAxisToSRSAxisMapping", oAxisMapping);
988 :
989 : const double dfCoordinateEpoch =
990 46 : poSRS->GetCoordinateEpoch();
991 46 : if (dfCoordinateEpoch > 0)
992 2 : oCRS.Set("coordinateEpoch", dfCoordinateEpoch);
993 : }
994 : else
995 : {
996 13 : oGeometryField.SetNull("coordinateSystem");
997 : }
998 :
999 59 : const auto &srsList = poLayer->GetSupportedSRSList(iGeom);
1000 59 : if (!srsList.empty())
1001 : {
1002 1 : CPLJSONArray oSupportedSRSList;
1003 3 : for (const auto &poSupportedSRS : srsList)
1004 : {
1005 : const char *pszAuthName =
1006 2 : poSupportedSRS->GetAuthorityName(nullptr);
1007 : const char *pszAuthCode =
1008 2 : poSupportedSRS->GetAuthorityCode(nullptr);
1009 4 : CPLJSONObject oSupportedSRS;
1010 2 : if (pszAuthName && pszAuthCode)
1011 : {
1012 4 : CPLJSONObject id;
1013 2 : id.Set("authority", pszAuthName);
1014 2 : id.Set("code", pszAuthCode);
1015 2 : oSupportedSRS.Add("id", id);
1016 4 : oSupportedSRSList.Add(oSupportedSRS);
1017 : }
1018 : else
1019 : {
1020 0 : char *pszWKT = nullptr;
1021 0 : poSupportedSRS->exportToWkt(&pszWKT,
1022 : apszWKTOptions);
1023 0 : if (pszWKT)
1024 : {
1025 0 : oSupportedSRS.Add("wkt", pszWKT);
1026 0 : oSupportedSRSList.Add(oSupportedSRS);
1027 : }
1028 0 : CPLFree(pszWKT);
1029 : }
1030 : }
1031 1 : oGeometryField.Add("supportedSRSList",
1032 : oSupportedSRSList);
1033 : }
1034 :
1035 : const auto &oCoordPrec =
1036 59 : poGFldDefn->GetCoordinatePrecision();
1037 59 : if (oCoordPrec.dfXYResolution !=
1038 : OGRGeomCoordinatePrecision::UNKNOWN)
1039 : {
1040 3 : oGeometryField.Add("xyCoordinateResolution",
1041 3 : oCoordPrec.dfXYResolution);
1042 : }
1043 59 : if (oCoordPrec.dfZResolution !=
1044 : OGRGeomCoordinatePrecision::UNKNOWN)
1045 : {
1046 3 : oGeometryField.Add("zCoordinateResolution",
1047 3 : oCoordPrec.dfZResolution);
1048 : }
1049 59 : if (oCoordPrec.dfMResolution !=
1050 : OGRGeomCoordinatePrecision::UNKNOWN)
1051 : {
1052 2 : oGeometryField.Add("mCoordinateResolution",
1053 2 : oCoordPrec.dfMResolution);
1054 : }
1055 :
1056 : // For example set by OpenFileGDB driver
1057 59 : if (!oCoordPrec.oFormatSpecificOptions.empty())
1058 : {
1059 2 : CPLJSONObject oFormatSpecificOptions;
1060 2 : for (const auto &formatOptionsPair :
1061 6 : oCoordPrec.oFormatSpecificOptions)
1062 : {
1063 4 : CPLJSONObject oThisFormatSpecificOptions;
1064 44 : for (const auto &[pszKey, pszValue] :
1065 : cpl::IterateNameValue(
1066 46 : formatOptionsPair.second))
1067 : {
1068 : const auto eValueType =
1069 22 : CPLGetValueType(pszValue);
1070 22 : if (eValueType == CPL_VALUE_INTEGER)
1071 : {
1072 14 : oThisFormatSpecificOptions.Add(
1073 : pszKey, CPLAtoGIntBig(pszValue));
1074 : }
1075 8 : else if (eValueType == CPL_VALUE_REAL)
1076 : {
1077 6 : oThisFormatSpecificOptions.Add(
1078 : pszKey, CPLAtof(pszValue));
1079 : }
1080 : else
1081 : {
1082 2 : oThisFormatSpecificOptions.Add(pszKey,
1083 : pszValue);
1084 : }
1085 : }
1086 2 : oFormatSpecificOptions.Add(
1087 2 : formatOptionsPair.first,
1088 : oThisFormatSpecificOptions);
1089 : }
1090 2 : oGeometryField.Add(
1091 : "coordinatePrecisionFormatSpecificOptions",
1092 : oFormatSpecificOptions);
1093 : }
1094 : }
1095 : else
1096 : {
1097 4 : Concat(osRet, psOptions->bStdoutOutput,
1098 : "Geometry (%s): %s\n", poGFldDefn->GetNameRef(),
1099 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1100 : }
1101 75 : }
1102 : }
1103 110 : else if (psOptions->bGeomType)
1104 : {
1105 110 : Concat(osRet, psOptions->bStdoutOutput, "Geometry: %s\n",
1106 110 : OGRGeometryTypeToName(poLayer->GetGeomType()));
1107 : }
1108 :
1109 185 : if (psOptions->bFeatureCount)
1110 : {
1111 164 : if (bJson)
1112 53 : oLayer.Set("featureCount", poLayer->GetFeatureCount());
1113 : else
1114 : {
1115 111 : Concat(osRet, psOptions->bStdoutOutput,
1116 : "Feature Count: " CPL_FRMT_GIB "\n",
1117 111 : poLayer->GetFeatureCount());
1118 : }
1119 : }
1120 :
1121 185 : if (!bJson && psOptions->bExtent && nGeomFieldCount > 1)
1122 : {
1123 6 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1124 : {
1125 4 : if (psOptions->bExtent3D)
1126 : {
1127 0 : OGREnvelope3D oExt;
1128 0 : if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) == OGRERR_NONE)
1129 : {
1130 : OGRGeomFieldDefn *poGFldDefn =
1131 0 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1132 0 : Concat(osRet, psOptions->bStdoutOutput,
1133 : "Extent (%s): (%f, %f, %s) - (%f, %f, %s)\n",
1134 : poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
1135 0 : std::isfinite(oExt.MinZ)
1136 0 : ? CPLSPrintf("%f", oExt.MinZ)
1137 : : "none",
1138 : oExt.MaxX, oExt.MaxY,
1139 0 : std::isfinite(oExt.MaxZ)
1140 0 : ? CPLSPrintf("%f", oExt.MaxZ)
1141 : : "none");
1142 : }
1143 : }
1144 : else
1145 : {
1146 4 : OGREnvelope oExt;
1147 4 : if (poLayer->GetExtent(iGeom, &oExt, TRUE) == OGRERR_NONE)
1148 : {
1149 : OGRGeomFieldDefn *poGFldDefn =
1150 4 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1151 4 : Concat(osRet, psOptions->bStdoutOutput,
1152 : "Extent (%s): (%f, %f) - (%f, %f)\n",
1153 : poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
1154 : oExt.MaxX, oExt.MaxY);
1155 : }
1156 : }
1157 2 : }
1158 : }
1159 183 : else if (!bJson && psOptions->bExtent)
1160 : {
1161 110 : if (psOptions->bExtent3D)
1162 : {
1163 2 : OGREnvelope3D oExt;
1164 2 : if (poLayer->GetExtent3D(0, &oExt, TRUE) == OGRERR_NONE)
1165 : {
1166 4 : Concat(
1167 2 : osRet, psOptions->bStdoutOutput,
1168 : "Extent: (%f, %f, %s) - (%f, %f, %s)\n", oExt.MinX,
1169 : oExt.MinY,
1170 3 : std::isfinite(oExt.MinZ) ? CPLSPrintf("%f", oExt.MinZ)
1171 : : "none",
1172 : oExt.MaxX, oExt.MaxY,
1173 3 : std::isfinite(oExt.MaxZ) ? CPLSPrintf("%f", oExt.MaxZ)
1174 : : "none");
1175 : }
1176 : }
1177 : else
1178 : {
1179 108 : OGREnvelope oExt;
1180 108 : if (poLayer->GetExtent(&oExt, TRUE) == OGRERR_NONE)
1181 : {
1182 30 : Concat(osRet, psOptions->bStdoutOutput,
1183 : "Extent: (%f, %f) - (%f, %f)\n", oExt.MinX,
1184 : oExt.MinY, oExt.MaxX, oExt.MaxY);
1185 : }
1186 : }
1187 : }
1188 :
1189 : const auto displayExtraInfoSRS =
1190 244 : [&osRet, &psOptions](const OGRSpatialReference *poSRS)
1191 : {
1192 40 : const double dfCoordinateEpoch = poSRS->GetCoordinateEpoch();
1193 40 : if (dfCoordinateEpoch > 0)
1194 : {
1195 : std::string osCoordinateEpoch =
1196 4 : CPLSPrintf("%f", dfCoordinateEpoch);
1197 2 : const size_t nDotPos = osCoordinateEpoch.find('.');
1198 2 : if (nDotPos != std::string::npos)
1199 : {
1200 22 : while (osCoordinateEpoch.size() > nDotPos + 2 &&
1201 10 : osCoordinateEpoch.back() == '0')
1202 10 : osCoordinateEpoch.pop_back();
1203 : }
1204 2 : Concat(osRet, psOptions->bStdoutOutput,
1205 : "Coordinate epoch: %s\n", osCoordinateEpoch.c_str());
1206 : }
1207 :
1208 40 : const auto &mapping = poSRS->GetDataAxisToSRSAxisMapping();
1209 40 : Concat(osRet, psOptions->bStdoutOutput,
1210 : "Data axis to CRS axis mapping: ");
1211 121 : for (size_t i = 0; i < mapping.size(); i++)
1212 : {
1213 81 : if (i > 0)
1214 : {
1215 41 : Concat(osRet, psOptions->bStdoutOutput, ",");
1216 : }
1217 81 : Concat(osRet, psOptions->bStdoutOutput, "%d", mapping[i]);
1218 : }
1219 40 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1220 40 : };
1221 :
1222 114 : const auto DisplaySupportedCRSList = [&](int iGeomField)
1223 : {
1224 114 : const auto &srsList = poLayer->GetSupportedSRSList(iGeomField);
1225 114 : if (!srsList.empty())
1226 : {
1227 1 : Concat(osRet, psOptions->bStdoutOutput, "Supported SRS: ");
1228 1 : bool bFirst = true;
1229 3 : for (const auto &poSupportedSRS : srsList)
1230 : {
1231 : const char *pszAuthName =
1232 2 : poSupportedSRS->GetAuthorityName(nullptr);
1233 : const char *pszAuthCode =
1234 2 : poSupportedSRS->GetAuthorityCode(nullptr);
1235 2 : if (!bFirst)
1236 1 : Concat(osRet, psOptions->bStdoutOutput, ", ");
1237 2 : bFirst = false;
1238 2 : if (pszAuthName && pszAuthCode)
1239 : {
1240 2 : Concat(osRet, psOptions->bStdoutOutput, "%s:%s",
1241 : pszAuthName, pszAuthCode);
1242 : }
1243 : else
1244 : {
1245 0 : ConcatStr(osRet, psOptions->bStdoutOutput,
1246 : poSupportedSRS->GetName());
1247 : }
1248 : }
1249 1 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1250 : }
1251 114 : };
1252 :
1253 185 : if (!bJson && nGeomFieldCount > 1)
1254 : {
1255 :
1256 6 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1257 : {
1258 : OGRGeomFieldDefn *poGFldDefn =
1259 4 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1260 4 : const OGRSpatialReference *poSRS = poGFldDefn->GetSpatialRef();
1261 4 : char *pszWKT = nullptr;
1262 4 : if (poSRS == nullptr)
1263 : {
1264 0 : pszWKT = CPLStrdup("(unknown)");
1265 : }
1266 : else
1267 : {
1268 4 : poSRS->exportToWkt(&pszWKT, apszWKTOptions);
1269 : }
1270 :
1271 4 : Concat(osRet, psOptions->bStdoutOutput, "SRS WKT (%s):\n%s\n",
1272 : poGFldDefn->GetNameRef(), pszWKT);
1273 4 : CPLFree(pszWKT);
1274 4 : if (poSRS)
1275 : {
1276 4 : displayExtraInfoSRS(poSRS);
1277 : }
1278 4 : DisplaySupportedCRSList(iGeom);
1279 2 : }
1280 : }
1281 183 : else if (!bJson)
1282 : {
1283 110 : char *pszWKT = nullptr;
1284 110 : auto poSRS = poLayer->GetSpatialRef();
1285 110 : if (poSRS == nullptr)
1286 : {
1287 74 : pszWKT = CPLStrdup("(unknown)");
1288 : }
1289 : else
1290 : {
1291 36 : poSRS->exportToWkt(&pszWKT, apszWKTOptions);
1292 : }
1293 :
1294 110 : Concat(osRet, psOptions->bStdoutOutput, "Layer SRS WKT:\n%s\n",
1295 : pszWKT);
1296 110 : CPLFree(pszWKT);
1297 110 : if (poSRS)
1298 : {
1299 36 : displayExtraInfoSRS(poSRS);
1300 : }
1301 110 : DisplaySupportedCRSList(0);
1302 : }
1303 :
1304 185 : const char *pszFIDColumn = poLayer->GetFIDColumn();
1305 185 : if (pszFIDColumn[0] != '\0')
1306 : {
1307 48 : if (bJson)
1308 33 : oLayer.Set("fidColumnName", pszFIDColumn);
1309 : else
1310 : {
1311 15 : Concat(osRet, psOptions->bStdoutOutput, "FID Column = %s\n",
1312 : pszFIDColumn);
1313 : }
1314 : }
1315 :
1316 193 : for (int iGeom = 0; !bJson && iGeom < nGeomFieldCount; iGeom++)
1317 : {
1318 : OGRGeomFieldDefn *poGFldDefn =
1319 40 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1320 72 : if (nGeomFieldCount == 1 && EQUAL(poGFldDefn->GetNameRef(), "") &&
1321 32 : poGFldDefn->IsNullable())
1322 32 : break;
1323 8 : Concat(osRet, psOptions->bStdoutOutput, "Geometry Column ");
1324 8 : if (nGeomFieldCount > 1)
1325 4 : Concat(osRet, psOptions->bStdoutOutput, "%d ", iGeom + 1);
1326 8 : if (!poGFldDefn->IsNullable())
1327 0 : Concat(osRet, psOptions->bStdoutOutput, "NOT NULL ");
1328 8 : Concat(osRet, psOptions->bStdoutOutput, "= %s\n",
1329 : poGFldDefn->GetNameRef());
1330 : }
1331 :
1332 370 : CPLJSONArray oFields;
1333 185 : if (bJson)
1334 73 : oLayer.Add("fields", oFields);
1335 791 : for (int iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++)
1336 : {
1337 606 : const OGRFieldDefn *poField = poDefn->GetFieldDefn(iAttr);
1338 606 : const char *pszAlias = poField->GetAlternativeNameRef();
1339 606 : const std::string &osDomain = poField->GetDomainName();
1340 606 : const std::string &osComment = poField->GetComment();
1341 606 : const auto eType = poField->GetType();
1342 1212 : std::string osTimeZone;
1343 606 : if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
1344 : {
1345 26 : const int nTZFlag = poField->GetTZFlag();
1346 26 : if (nTZFlag == OGR_TZFLAG_LOCALTIME)
1347 : {
1348 1 : osTimeZone = "localtime";
1349 : }
1350 25 : else if (nTZFlag == OGR_TZFLAG_MIXED_TZ)
1351 : {
1352 1 : osTimeZone = "mixed timezones";
1353 : }
1354 24 : else if (nTZFlag == OGR_TZFLAG_UTC)
1355 : {
1356 2 : osTimeZone = "UTC";
1357 : }
1358 22 : else if (nTZFlag > 0)
1359 : {
1360 : char chSign;
1361 3 : const int nOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
1362 3 : int nHours =
1363 : static_cast<int>(nOffset / 60); // Round towards zero.
1364 3 : const int nMinutes = std::abs(nOffset - nHours * 60);
1365 :
1366 3 : if (nOffset < 0)
1367 : {
1368 1 : chSign = '-';
1369 1 : nHours = std::abs(nHours);
1370 : }
1371 : else
1372 : {
1373 2 : chSign = '+';
1374 : }
1375 : osTimeZone =
1376 3 : CPLSPrintf("%c%02d:%02d", chSign, nHours, nMinutes);
1377 : }
1378 : }
1379 :
1380 606 : if (bJson)
1381 : {
1382 236 : CPLJSONObject oField;
1383 118 : oFields.Add(oField);
1384 118 : oField.Set("name", poField->GetNameRef());
1385 118 : oField.Set("type", OGRFieldDefn::GetFieldTypeName(eType));
1386 118 : if (poField->GetSubType() != OFSTNone)
1387 2 : oField.Set("subType", OGRFieldDefn::GetFieldSubTypeName(
1388 : poField->GetSubType()));
1389 118 : if (poField->GetWidth() > 0)
1390 63 : oField.Set("width", poField->GetWidth());
1391 118 : if (poField->GetPrecision() > 0)
1392 12 : oField.Set("precision", poField->GetPrecision());
1393 118 : oField.Set("nullable", CPL_TO_BOOL(poField->IsNullable()));
1394 118 : oField.Set("uniqueConstraint",
1395 118 : CPL_TO_BOOL(poField->IsUnique()));
1396 118 : if (poField->GetDefault() != nullptr)
1397 2 : oField.Set("defaultValue", poField->GetDefault());
1398 118 : if (pszAlias != nullptr && pszAlias[0])
1399 1 : oField.Set("alias", pszAlias);
1400 118 : if (!osDomain.empty())
1401 6 : oField.Set("domainName", osDomain);
1402 118 : if (!osComment.empty())
1403 1 : oField.Set("comment", osComment);
1404 118 : if (!osTimeZone.empty())
1405 7 : oField.Set("timezone", osTimeZone);
1406 : }
1407 : else
1408 : {
1409 : const char *pszType =
1410 488 : (poField->GetSubType() != OFSTNone)
1411 488 : ? CPLSPrintf("%s(%s)",
1412 : OGRFieldDefn::GetFieldTypeName(
1413 : poField->GetType()),
1414 : OGRFieldDefn::GetFieldSubTypeName(
1415 : poField->GetSubType()))
1416 466 : : OGRFieldDefn::GetFieldTypeName(poField->GetType());
1417 488 : Concat(osRet, psOptions->bStdoutOutput, "%s: %s",
1418 : poField->GetNameRef(), pszType);
1419 488 : if (eType == OFTTime || eType == OFTDate ||
1420 : eType == OFTDateTime)
1421 : {
1422 18 : if (!osTimeZone.empty())
1423 0 : Concat(osRet, psOptions->bStdoutOutput, " (%s)",
1424 : osTimeZone.c_str());
1425 : }
1426 : else
1427 : {
1428 470 : Concat(osRet, psOptions->bStdoutOutput, " (%d.%d)",
1429 : poField->GetWidth(), poField->GetPrecision());
1430 : }
1431 488 : if (poField->IsUnique())
1432 0 : Concat(osRet, psOptions->bStdoutOutput, " UNIQUE");
1433 488 : if (!poField->IsNullable())
1434 204 : Concat(osRet, psOptions->bStdoutOutput, " NOT NULL");
1435 488 : if (poField->GetDefault() != nullptr)
1436 8 : Concat(osRet, psOptions->bStdoutOutput, " DEFAULT %s",
1437 : poField->GetDefault());
1438 488 : if (pszAlias != nullptr && pszAlias[0])
1439 0 : Concat(osRet, psOptions->bStdoutOutput,
1440 : ", alternative name=\"%s\"", pszAlias);
1441 488 : if (!osDomain.empty())
1442 5 : Concat(osRet, psOptions->bStdoutOutput, ", domain name=%s",
1443 : osDomain.c_str());
1444 488 : if (!osComment.empty())
1445 0 : Concat(osRet, psOptions->bStdoutOutput, ", comment=%s",
1446 : osComment.c_str());
1447 488 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1448 : }
1449 : }
1450 : }
1451 :
1452 : /* -------------------------------------------------------------------- */
1453 : /* Read, and dump features. */
1454 : /* -------------------------------------------------------------------- */
1455 :
1456 186 : if ((psOptions->nFetchFID == OGRNullFID || bJson) && !bForceSummary &&
1457 185 : ((psOptions->bIsCli && psOptions->bFeaturesUserRequested) ||
1458 180 : (!psOptions->bIsCli && !psOptions->bSummaryOnly)))
1459 : {
1460 89 : if (!psOptions->bSuperQuiet)
1461 : {
1462 178 : CPLJSONArray oFeatures;
1463 : const bool bDisplayFields =
1464 89 : CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
1465 : "DISPLAY_FIELDS", "YES"));
1466 : const int nFields =
1467 89 : bDisplayFields ? poLayer->GetLayerDefn()->GetFieldCount() : 0;
1468 : const bool bDisplayGeometry =
1469 89 : CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
1470 : "DISPLAY_GEOMETRY", "YES"));
1471 : const int nGeomFields =
1472 89 : bDisplayGeometry ? poLayer->GetLayerDefn()->GetGeomFieldCount()
1473 89 : : 0;
1474 89 : if (bJson)
1475 12 : oLayer.Add("features", oFeatures);
1476 :
1477 : const auto EmitFeatureJSON =
1478 45 : [poLayer, nFields, nGeomFields,
1479 290 : &oFeatures](const OGRFeature *poFeature)
1480 : {
1481 90 : CPLJSONObject oFeature;
1482 90 : CPLJSONObject oProperties;
1483 45 : oFeatures.Add(oFeature);
1484 45 : oFeature.Add("type", "Feature");
1485 45 : oFeature.Add("properties", oProperties);
1486 45 : oFeature.Add("fid", poFeature->GetFID());
1487 157 : for (int i = 0; i < nFields; ++i)
1488 : {
1489 112 : const auto poFDefn = poFeature->GetFieldDefnRef(i);
1490 112 : const auto eType = poFDefn->GetType();
1491 112 : if (!poFeature->IsFieldSet(i))
1492 0 : continue;
1493 112 : if (poFeature->IsFieldNull(i))
1494 : {
1495 2 : oProperties.SetNull(poFDefn->GetNameRef());
1496 : }
1497 110 : else if (eType == OFTInteger)
1498 : {
1499 1 : if (poFDefn->GetSubType() == OFSTBoolean)
1500 0 : oProperties.Add(
1501 : poFDefn->GetNameRef(),
1502 0 : CPL_TO_BOOL(poFeature->GetFieldAsInteger(i)));
1503 : else
1504 1 : oProperties.Add(poFDefn->GetNameRef(),
1505 : poFeature->GetFieldAsInteger(i));
1506 : }
1507 109 : else if (eType == OFTInteger64)
1508 : {
1509 34 : oProperties.Add(poFDefn->GetNameRef(),
1510 : poFeature->GetFieldAsInteger64(i));
1511 : }
1512 75 : else if (eType == OFTReal)
1513 : {
1514 34 : oProperties.Add(poFDefn->GetNameRef(),
1515 : poFeature->GetFieldAsDouble(i));
1516 : }
1517 41 : else if ((eType == OFTString &&
1518 46 : poFDefn->GetSubType() != OFSTJSON) ||
1519 82 : eType == OFTDate || eType == OFTTime ||
1520 : eType == OFTDateTime)
1521 : {
1522 36 : oProperties.Add(poFDefn->GetNameRef(),
1523 : poFeature->GetFieldAsString(i));
1524 : }
1525 : else
1526 : {
1527 : char *pszSerialized =
1528 5 : poFeature->GetFieldAsSerializedJSon(i);
1529 5 : if (pszSerialized)
1530 : {
1531 : const auto eStrType =
1532 5 : CPLGetValueType(pszSerialized);
1533 5 : if (eStrType == CPL_VALUE_INTEGER)
1534 : {
1535 1 : oProperties.Add(poFDefn->GetNameRef(),
1536 : CPLAtoGIntBig(pszSerialized));
1537 : }
1538 4 : else if (eStrType == CPL_VALUE_REAL)
1539 : {
1540 0 : oProperties.Add(poFDefn->GetNameRef(),
1541 : CPLAtof(pszSerialized));
1542 : }
1543 : else
1544 : {
1545 8 : CPLJSONDocument oDoc;
1546 4 : if (oDoc.LoadMemory(pszSerialized))
1547 4 : oProperties.Add(poFDefn->GetNameRef(),
1548 8 : oDoc.GetRoot());
1549 : }
1550 5 : CPLFree(pszSerialized);
1551 : }
1552 : }
1553 : }
1554 :
1555 86 : const auto GetGeoJSONOptions = [poLayer](int iGeomField)
1556 : {
1557 43 : CPLStringList aosGeoJSONOptions;
1558 43 : const auto &oCoordPrec = poLayer->GetLayerDefn()
1559 43 : ->GetGeomFieldDefn(iGeomField)
1560 43 : ->GetCoordinatePrecision();
1561 43 : if (oCoordPrec.dfXYResolution !=
1562 : OGRGeomCoordinatePrecision::UNKNOWN)
1563 : {
1564 : aosGeoJSONOptions.SetNameValue(
1565 : "XY_COORD_PRECISION",
1566 : CPLSPrintf("%d",
1567 : OGRGeomCoordinatePrecision::
1568 : ResolutionToPrecision(
1569 1 : oCoordPrec.dfXYResolution)));
1570 : }
1571 43 : if (oCoordPrec.dfZResolution !=
1572 : OGRGeomCoordinatePrecision::UNKNOWN)
1573 : {
1574 : aosGeoJSONOptions.SetNameValue(
1575 : "Z_COORD_PRECISION",
1576 : CPLSPrintf("%d",
1577 : OGRGeomCoordinatePrecision::
1578 : ResolutionToPrecision(
1579 1 : oCoordPrec.dfZResolution)));
1580 : }
1581 43 : return aosGeoJSONOptions;
1582 45 : };
1583 :
1584 45 : if (nGeomFields == 0)
1585 2 : oFeature.SetNull("geometry");
1586 : else
1587 : {
1588 43 : if (const auto poGeom = poFeature->GetGeometryRef())
1589 : {
1590 : char *pszSerialized =
1591 43 : wkbFlatten(poGeom->getGeometryType()) <=
1592 : wkbGeometryCollection
1593 86 : ? poGeom->exportToJson(
1594 86 : GetGeoJSONOptions(0).List())
1595 43 : : nullptr;
1596 43 : if (pszSerialized)
1597 : {
1598 86 : CPLJSONDocument oDoc;
1599 43 : if (oDoc.LoadMemory(pszSerialized))
1600 43 : oFeature.Add("geometry", oDoc.GetRoot());
1601 43 : CPLFree(pszSerialized);
1602 : }
1603 : else
1604 : {
1605 0 : CPLJSONObject oGeometry;
1606 0 : oFeature.SetNull("geometry");
1607 0 : oFeature.Add("wkt_geometry", poGeom->exportToWkt());
1608 : }
1609 : }
1610 : else
1611 0 : oFeature.SetNull("geometry");
1612 :
1613 43 : if (nGeomFields > 1)
1614 : {
1615 0 : CPLJSONArray oGeometries;
1616 0 : oFeature.Add("geometries", oGeometries);
1617 0 : for (int i = 0; i < nGeomFields; ++i)
1618 : {
1619 0 : auto poGeom = poFeature->GetGeomFieldRef(i);
1620 0 : if (poGeom)
1621 : {
1622 : char *pszSerialized =
1623 0 : wkbFlatten(poGeom->getGeometryType()) <=
1624 : wkbGeometryCollection
1625 0 : ? poGeom->exportToJson(
1626 0 : GetGeoJSONOptions(i).List())
1627 0 : : nullptr;
1628 0 : if (pszSerialized)
1629 : {
1630 0 : CPLJSONDocument oDoc;
1631 0 : if (oDoc.LoadMemory(pszSerialized))
1632 0 : oGeometries.Add(oDoc.GetRoot());
1633 0 : CPLFree(pszSerialized);
1634 : }
1635 : else
1636 : {
1637 0 : CPLJSONObject oGeometry;
1638 0 : oGeometries.Add(poGeom->exportToWkt());
1639 : }
1640 : }
1641 : else
1642 0 : oGeometries.AddNull();
1643 : }
1644 : }
1645 : }
1646 45 : };
1647 :
1648 89 : if (psOptions->nFetchFID != OGRNullFID)
1649 : {
1650 : auto poFeature = std::unique_ptr<OGRFeature>(
1651 2 : poLayer->GetFeature(psOptions->nFetchFID));
1652 1 : if (poFeature)
1653 : {
1654 1 : EmitFeatureJSON(poFeature.get());
1655 : }
1656 : }
1657 88 : else if (psOptions->nLimit < 0 || psOptions->nLimit > 0)
1658 : {
1659 88 : GIntBig nFeatureCount = 0;
1660 728 : for (auto &poFeature : poLayer)
1661 : {
1662 640 : if (bJson)
1663 : {
1664 44 : EmitFeatureJSON(poFeature.get());
1665 : }
1666 : else
1667 : {
1668 596 : ConcatStr(osRet, psOptions->bStdoutOutput,
1669 : poFeature
1670 1788 : ->DumpReadableAsString(
1671 596 : psOptions->aosOptions.List())
1672 : .c_str());
1673 : }
1674 :
1675 640 : ++nFeatureCount;
1676 640 : if (psOptions->nLimit >= 0 &&
1677 3 : nFeatureCount >= psOptions->nLimit)
1678 : {
1679 2 : break;
1680 : }
1681 : }
1682 : }
1683 89 : }
1684 : }
1685 97 : else if (!bJson && psOptions->nFetchFID != OGRNullFID)
1686 : {
1687 : auto poFeature = std::unique_ptr<OGRFeature>(
1688 2 : poLayer->GetFeature(psOptions->nFetchFID));
1689 1 : if (poFeature == nullptr)
1690 : {
1691 0 : Concat(osRet, psOptions->bStdoutOutput,
1692 : "Unable to locate feature id " CPL_FRMT_GIB
1693 : " on this layer.\n",
1694 0 : psOptions->nFetchFID);
1695 : }
1696 : else
1697 : {
1698 1 : ConcatStr(
1699 1 : osRet, psOptions->bStdoutOutput,
1700 2 : poFeature->DumpReadableAsString(psOptions->aosOptions.List())
1701 : .c_str());
1702 : }
1703 : }
1704 : }
1705 :
1706 : /************************************************************************/
1707 : /* PrintLayerSummary() */
1708 : /************************************************************************/
1709 :
1710 23 : static void PrintLayerSummary(CPLString &osRet, CPLJSONObject &oLayer,
1711 : const GDALVectorInfoOptions *psOptions,
1712 : OGRLayer *poLayer, bool bIsPrivate)
1713 : {
1714 23 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1715 23 : const bool bIsSummaryCli = psOptions->bIsCli && psOptions->bSummaryOnly;
1716 23 : if (bJson)
1717 : {
1718 2 : oLayer.Set("name", poLayer->GetName());
1719 : }
1720 : else
1721 21 : ConcatStr(osRet, psOptions->bStdoutOutput, poLayer->GetName());
1722 :
1723 23 : const char *pszTitle = poLayer->GetMetadataItem("TITLE");
1724 23 : if (pszTitle)
1725 : {
1726 0 : if (bJson)
1727 0 : oLayer.Set("title", pszTitle);
1728 : else
1729 0 : Concat(osRet, psOptions->bStdoutOutput, " (title: %s)", pszTitle);
1730 : }
1731 :
1732 : const int nGeomFieldCount =
1733 23 : psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
1734 :
1735 23 : if (bIsSummaryCli && bJson)
1736 : {
1737 2 : CPLJSONArray oGeometryTypes;
1738 7 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1739 : {
1740 : OGRGeomFieldDefn *poGFldDefn =
1741 5 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1742 5 : oGeometryTypes.Add(OGRGeometryTypeToName(poGFldDefn->GetType()));
1743 : }
1744 2 : oLayer.Add("geometryType", oGeometryTypes);
1745 2 : return;
1746 : }
1747 :
1748 21 : if (bJson || nGeomFieldCount > 1)
1749 : {
1750 2 : if (!bJson)
1751 2 : Concat(osRet, psOptions->bStdoutOutput, " (");
1752 4 : CPLJSONArray oGeometryFields;
1753 2 : oLayer.Add("geometryFields", oGeometryFields);
1754 8 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1755 : {
1756 : OGRGeomFieldDefn *poGFldDefn =
1757 6 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1758 6 : if (bJson)
1759 : {
1760 0 : oGeometryFields.Add(
1761 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1762 : }
1763 : else
1764 : {
1765 6 : if (iGeom > 0)
1766 4 : Concat(osRet, psOptions->bStdoutOutput, ", ");
1767 6 : ConcatStr(osRet, psOptions->bStdoutOutput,
1768 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1769 : }
1770 : }
1771 2 : if (!bJson)
1772 4 : Concat(osRet, psOptions->bStdoutOutput, ")");
1773 : }
1774 19 : else if (psOptions->bGeomType && poLayer->GetGeomType() != wkbUnknown)
1775 11 : Concat(osRet, psOptions->bStdoutOutput, " (%s)",
1776 11 : OGRGeometryTypeToName(poLayer->GetGeomType()));
1777 :
1778 21 : if (bIsPrivate)
1779 : {
1780 0 : if (bJson)
1781 0 : oLayer.Set("isPrivate", true);
1782 : else
1783 0 : Concat(osRet, psOptions->bStdoutOutput, " [private]");
1784 : }
1785 :
1786 21 : if (!bJson)
1787 21 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1788 : }
1789 :
1790 : /************************************************************************/
1791 : /* ReportHiearchicalLayers() */
1792 : /************************************************************************/
1793 :
1794 5 : static void ReportHiearchicalLayers(CPLString &osRet, CPLJSONObject &oRoot,
1795 : const GDALVectorInfoOptions *psOptions,
1796 : const GDALGroup *group,
1797 : const std::string &indent, bool bGeomType)
1798 : {
1799 5 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1800 10 : const auto aosVectorLayerNames = group->GetVectorLayerNames();
1801 10 : CPLJSONArray oLayerNames;
1802 5 : oRoot.Add("layerNames", oLayerNames);
1803 23 : for (const auto &osVectorLayerName : aosVectorLayerNames)
1804 : {
1805 18 : OGRLayer *poLayer = group->OpenVectorLayer(osVectorLayerName);
1806 18 : if (poLayer)
1807 : {
1808 36 : CPLJSONObject oLayer;
1809 18 : if (!bJson)
1810 : {
1811 4 : Concat(osRet, psOptions->bStdoutOutput,
1812 : "%sLayer: ", indent.c_str());
1813 4 : PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
1814 : /* bIsPrivate=*/false);
1815 : }
1816 : else
1817 : {
1818 14 : oLayerNames.Add(poLayer->GetName());
1819 : }
1820 : }
1821 : }
1822 :
1823 10 : const std::string subIndent(indent + " ");
1824 10 : auto aosSubGroupNames = group->GetGroupNames();
1825 10 : CPLJSONArray oGroupArray;
1826 5 : oRoot.Add("groups", oGroupArray);
1827 7 : for (const auto &osSubGroupName : aosSubGroupNames)
1828 : {
1829 4 : auto poSubGroup = group->OpenGroup(osSubGroupName);
1830 2 : if (poSubGroup)
1831 : {
1832 4 : CPLJSONObject oGroup;
1833 2 : if (!bJson)
1834 : {
1835 2 : Concat(osRet, psOptions->bStdoutOutput, "Group %s",
1836 : indent.c_str());
1837 2 : Concat(osRet, psOptions->bStdoutOutput, "%s:\n",
1838 : osSubGroupName.c_str());
1839 : }
1840 : else
1841 : {
1842 0 : oGroupArray.Add(oGroup);
1843 0 : oGroup.Set("name", osSubGroupName);
1844 : }
1845 2 : ReportHiearchicalLayers(osRet, oGroup, psOptions, poSubGroup.get(),
1846 : subIndent, bGeomType);
1847 : }
1848 : }
1849 5 : }
1850 :
1851 : /************************************************************************/
1852 : /* GDALVectorInfo() */
1853 : /************************************************************************/
1854 :
1855 : /**
1856 : * Lists various information about a GDAL supported vector dataset.
1857 : *
1858 : * This is the equivalent of the <a href="/programs/ogrinfo.html">ogrinfo</a>
1859 : * utility.
1860 : *
1861 : * GDALVectorInfoOptions* must be allocated and freed with
1862 : * GDALVectorInfoOptionsNew() and GDALVectorInfoOptionsFree() respectively.
1863 : *
1864 : * @param hDataset the dataset handle.
1865 : * @param psOptions the options structure returned by GDALVectorInfoOptionsNew()
1866 : * or NULL.
1867 : * @return string corresponding to the information about the raster dataset
1868 : * (must be freed with CPLFree()), or NULL in case of error.
1869 : *
1870 : * @since GDAL 3.7
1871 : */
1872 117 : char *GDALVectorInfo(GDALDatasetH hDataset,
1873 : const GDALVectorInfoOptions *psOptions)
1874 : {
1875 117 : auto poDS = GDALDataset::FromHandle(hDataset);
1876 117 : if (poDS == nullptr)
1877 0 : return nullptr;
1878 :
1879 234 : const GDALVectorInfoOptions sDefaultOptions;
1880 117 : if (!psOptions)
1881 0 : psOptions = &sDefaultOptions;
1882 :
1883 117 : GDALDriver *poDriver = poDS->GetDriver();
1884 :
1885 234 : CPLString osRet;
1886 234 : CPLJSONObject oRoot;
1887 234 : const std::string osFilename(poDS->GetDescription());
1888 :
1889 117 : const bool bExportOgrSchema = psOptions->bExportOgrSchema;
1890 117 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1891 117 : const bool bIsSummaryCli =
1892 117 : (psOptions->bIsCli && psOptions->bSummaryUserRequested);
1893 :
1894 234 : CPLJSONArray oLayerArray;
1895 117 : if (bJson)
1896 : {
1897 54 : if (!bExportOgrSchema)
1898 : {
1899 44 : oRoot.Set("description", poDS->GetDescription());
1900 44 : if (poDriver)
1901 : {
1902 43 : oRoot.Set("driverShortName", poDriver->GetDescription());
1903 43 : oRoot.Set("driverLongName",
1904 86 : poDriver->GetMetadataItem(GDAL_DMD_LONGNAME));
1905 : }
1906 : }
1907 54 : oRoot.Add("layers", oLayerArray);
1908 : }
1909 :
1910 : /* -------------------------------------------------------------------- */
1911 : /* Some information messages. */
1912 : /* -------------------------------------------------------------------- */
1913 117 : if (!bJson && psOptions->bVerbose)
1914 : {
1915 122 : Concat(osRet, psOptions->bStdoutOutput,
1916 : "INFO: Open of `%s'\n"
1917 : " using driver `%s' successful.\n",
1918 : osFilename.c_str(),
1919 61 : poDriver ? poDriver->GetDescription() : "(null)");
1920 : }
1921 :
1922 178 : if (!bJson && psOptions->bVerbose &&
1923 61 : !EQUAL(osFilename.c_str(), poDS->GetDescription()))
1924 : {
1925 0 : Concat(osRet, psOptions->bStdoutOutput,
1926 : "INFO: Internal data source name `%s'\n"
1927 : " different from user name `%s'.\n",
1928 0 : poDS->GetDescription(), osFilename.c_str());
1929 : }
1930 :
1931 117 : int nRepeatCount = psOptions->nRepeatCount;
1932 :
1933 117 : if (!bIsSummaryCli && !bExportOgrSchema)
1934 : {
1935 103 : GDALVectorInfoReportMetadata(
1936 103 : osRet, oRoot, psOptions, poDS, psOptions->bListMDD,
1937 103 : psOptions->bShowMetadata, psOptions->aosExtraMDDomains.List());
1938 :
1939 103 : CPLJSONObject oDomains;
1940 103 : oRoot.Add("domains", oDomains);
1941 103 : if (!psOptions->osFieldDomain.empty())
1942 : {
1943 7 : auto poDomain = poDS->GetFieldDomain(psOptions->osFieldDomain);
1944 7 : if (poDomain == nullptr)
1945 : {
1946 0 : CPLError(CE_Failure, CPLE_AppDefined,
1947 : "Domain %s cannot be found.",
1948 : psOptions->osFieldDomain.c_str());
1949 0 : return nullptr;
1950 : }
1951 7 : if (!bJson)
1952 7 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1953 7 : ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
1954 7 : if (!bJson)
1955 7 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1956 : }
1957 96 : else if (bJson)
1958 : {
1959 49 : for (const auto &osDomainName : poDS->GetFieldDomainNames())
1960 : {
1961 7 : auto poDomain = poDS->GetFieldDomain(osDomainName);
1962 7 : if (poDomain)
1963 : {
1964 7 : ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
1965 : }
1966 : }
1967 : }
1968 :
1969 103 : if (psOptions->bDatasetGetNextFeature)
1970 : {
1971 1 : nRepeatCount = 0; // skip layer reporting.
1972 :
1973 : /* --------------------------------------------------------------------
1974 : */
1975 : /* Set filters if provided. */
1976 : /* --------------------------------------------------------------------
1977 : */
1978 2 : if (!psOptions->osWHERE.empty() ||
1979 1 : psOptions->poSpatialFilter != nullptr)
1980 : {
1981 0 : for (int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++)
1982 : {
1983 0 : OGRLayer *poLayer = poDS->GetLayer(iLayer);
1984 :
1985 0 : if (poLayer == nullptr)
1986 : {
1987 0 : CPLError(CE_Failure, CPLE_AppDefined,
1988 : "Couldn't fetch advertised layer %d.", iLayer);
1989 0 : return nullptr;
1990 : }
1991 :
1992 0 : if (!psOptions->osWHERE.empty())
1993 : {
1994 0 : if (poLayer->SetAttributeFilter(
1995 0 : psOptions->osWHERE.c_str()) != OGRERR_NONE)
1996 : {
1997 0 : CPLError(
1998 : CE_Warning, CPLE_AppDefined,
1999 : "SetAttributeFilter(%s) failed on layer %s.",
2000 0 : psOptions->osWHERE.c_str(), poLayer->GetName());
2001 : }
2002 : }
2003 :
2004 0 : if (psOptions->poSpatialFilter != nullptr)
2005 : {
2006 0 : if (!psOptions->osGeomField.empty())
2007 : {
2008 0 : OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
2009 0 : const int iGeomField = poDefn->GetGeomFieldIndex(
2010 0 : psOptions->osGeomField.c_str());
2011 0 : if (iGeomField >= 0)
2012 0 : poLayer->SetSpatialFilter(
2013 : iGeomField,
2014 0 : psOptions->poSpatialFilter.get());
2015 : else
2016 0 : CPLError(CE_Warning, CPLE_AppDefined,
2017 : "Cannot find geometry field %s.",
2018 : psOptions->osGeomField.c_str());
2019 : }
2020 : else
2021 : {
2022 0 : poLayer->SetSpatialFilter(
2023 0 : psOptions->poSpatialFilter.get());
2024 : }
2025 : }
2026 : }
2027 : }
2028 :
2029 1 : std::set<OGRLayer *> oSetLayers;
2030 : while (true)
2031 : {
2032 11 : OGRLayer *poLayer = nullptr;
2033 : OGRFeature *poFeature =
2034 11 : poDS->GetNextFeature(&poLayer, nullptr, nullptr, nullptr);
2035 11 : if (poFeature == nullptr)
2036 1 : break;
2037 10 : if (psOptions->aosLayers.empty() || poLayer == nullptr ||
2038 0 : CSLFindString(psOptions->aosLayers.List(),
2039 0 : poLayer->GetName()) >= 0)
2040 : {
2041 10 : if (psOptions->bVerbose && poLayer != nullptr &&
2042 10 : oSetLayers.find(poLayer) == oSetLayers.end())
2043 : {
2044 0 : oSetLayers.insert(poLayer);
2045 0 : CPLJSONObject oLayer;
2046 0 : oLayerArray.Add(oLayer);
2047 0 : ReportOnLayer(
2048 : osRet, oLayer, psOptions, poLayer,
2049 : /*bForceSummary = */ true,
2050 : /*bTakeIntoAccountWHERE = */ false,
2051 : /*bTakeIntoAccountSpatialFilter = */ false,
2052 : /*bTakeIntoAccountGeomField = */ false);
2053 : }
2054 10 : if (!psOptions->bSuperQuiet && !psOptions->bSummaryOnly)
2055 10 : poFeature->DumpReadable(
2056 : nullptr,
2057 : const_cast<char **>(psOptions->aosOptions.List()));
2058 : }
2059 10 : OGRFeature::DestroyFeature(poFeature);
2060 10 : }
2061 : }
2062 :
2063 : /* -------------------------------------------------------------------- */
2064 : /* Special case for -sql clause. No source layers required. */
2065 : /* -------------------------------------------------------------------- */
2066 102 : else if (!psOptions->osSQLStatement.empty())
2067 : {
2068 5 : nRepeatCount = 0; // skip layer reporting.
2069 :
2070 5 : if (!bJson && !psOptions->aosLayers.empty())
2071 0 : Concat(osRet, psOptions->bStdoutOutput,
2072 : "layer names ignored in combination with -sql.\n");
2073 :
2074 5 : CPLErrorReset();
2075 15 : OGRLayer *poResultSet = poDS->ExecuteSQL(
2076 : psOptions->osSQLStatement.c_str(),
2077 5 : psOptions->osGeomField.empty()
2078 5 : ? psOptions->poSpatialFilter.get()
2079 : : nullptr,
2080 5 : psOptions->osDialect.empty() ? nullptr
2081 6 : : psOptions->osDialect.c_str());
2082 :
2083 5 : if (poResultSet != nullptr)
2084 : {
2085 4 : if (!psOptions->osWHERE.empty())
2086 : {
2087 0 : if (poResultSet->SetAttributeFilter(
2088 0 : psOptions->osWHERE.c_str()) != OGRERR_NONE)
2089 : {
2090 0 : CPLError(CE_Failure, CPLE_AppDefined,
2091 : "SetAttributeFilter(%s) failed.",
2092 : psOptions->osWHERE.c_str());
2093 0 : return nullptr;
2094 : }
2095 : }
2096 :
2097 8 : CPLJSONObject oLayer;
2098 4 : oLayerArray.Add(oLayer);
2099 4 : if (!psOptions->osGeomField.empty())
2100 0 : ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
2101 : /*bForceSummary = */ false,
2102 : /*bTakeIntoAccountWHERE = */ false,
2103 : /*bTakeIntoAccountSpatialFilter = */ true,
2104 : /*bTakeIntoAccountGeomField = */ true);
2105 : else
2106 4 : ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
2107 : /*bForceSummary = */ false,
2108 : /*bTakeIntoAccountWHERE = */ false,
2109 : /*bTakeIntoAccountSpatialFilter = */ false,
2110 : /*bTakeIntoAccountGeomField = */ false);
2111 :
2112 4 : poDS->ReleaseResultSet(poResultSet);
2113 : }
2114 1 : else if (CPLGetLastErrorType() != CE_None)
2115 : {
2116 1 : return nullptr;
2117 : }
2118 : }
2119 : }
2120 :
2121 : // coverity[tainted_data]
2122 116 : auto papszLayers = psOptions->aosLayers.List();
2123 226 : for (int iRepeat = 0; iRepeat < nRepeatCount; iRepeat++)
2124 : {
2125 111 : if (papszLayers == nullptr || papszLayers[0] == nullptr)
2126 : {
2127 100 : const int nLayerCount = poDS->GetLayerCount();
2128 100 : if (iRepeat == 0)
2129 100 : CPLDebug("OGR", "GetLayerCount() = %d\n", nLayerCount);
2130 :
2131 100 : bool bDone = false;
2132 100 : auto poRootGroup = poDS->GetRootGroup();
2133 103 : if ((bJson || !psOptions->bAllLayers) && poRootGroup &&
2134 103 : (!poRootGroup->GetGroupNames().empty() ||
2135 102 : !poRootGroup->GetVectorLayerNames().empty()))
2136 : {
2137 6 : CPLJSONObject oGroup;
2138 3 : oRoot.Add("rootGroup", oGroup);
2139 3 : ReportHiearchicalLayers(osRet, oGroup, psOptions,
2140 6 : poRootGroup.get(), std::string(),
2141 3 : psOptions->bGeomType);
2142 3 : if (!bJson)
2143 1 : bDone = true;
2144 : }
2145 :
2146 : /* --------------------------------------------------------------------
2147 : */
2148 : /* Process each data source layer. */
2149 : /* --------------------------------------------------------------------
2150 : */
2151 290 : for (int iLayer = 0; !bDone && iLayer < nLayerCount; iLayer++)
2152 : {
2153 190 : OGRLayer *poLayer = poDS->GetLayer(iLayer);
2154 :
2155 190 : if (poLayer == nullptr)
2156 : {
2157 0 : CPLError(CE_Failure, CPLE_AppDefined,
2158 : "Couldn't fetch advertised layer %d.", iLayer);
2159 0 : return nullptr;
2160 : }
2161 :
2162 380 : CPLJSONObject oLayer;
2163 190 : oLayerArray.Add(oLayer);
2164 190 : if (!psOptions->bAllLayers || bIsSummaryCli)
2165 : {
2166 19 : if (!bJson)
2167 17 : Concat(osRet, psOptions->bStdoutOutput,
2168 : "%d: ", iLayer + 1);
2169 19 : PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
2170 19 : poDS->IsLayerPrivate(iLayer));
2171 : }
2172 : else
2173 : {
2174 171 : if (iRepeat != 0)
2175 0 : poLayer->ResetReading();
2176 :
2177 171 : ReportOnLayer(osRet, oLayer, psOptions, poLayer,
2178 : /*bForceSummary = */ false,
2179 : /*bTakeIntoAccountWHERE = */ true,
2180 : /*bTakeIntoAccountSpatialFilter = */ true,
2181 : /*bTakeIntoAccountGeomField = */ true);
2182 : }
2183 100 : }
2184 : }
2185 : else
2186 : {
2187 : /* --------------------------------------------------------------------
2188 : */
2189 : /* Process specified data source layers. */
2190 : /* --------------------------------------------------------------------
2191 : */
2192 :
2193 22 : for (const char *pszLayer : cpl::Iterate(papszLayers))
2194 : {
2195 12 : OGRLayer *poLayer = poDS->GetLayerByName(pszLayer);
2196 :
2197 12 : if (poLayer == nullptr)
2198 : {
2199 1 : CPLError(CE_Failure, CPLE_AppDefined,
2200 : "Couldn't fetch requested layer %s.", pszLayer);
2201 1 : return nullptr;
2202 : }
2203 :
2204 11 : if (iRepeat != 0)
2205 0 : poLayer->ResetReading();
2206 :
2207 22 : CPLJSONObject oLayer;
2208 11 : oLayerArray.Add(oLayer);
2209 11 : ReportOnLayer(osRet, oLayer, psOptions, poLayer,
2210 : /*bForceSummary = */ false,
2211 : /*bTakeIntoAccountWHERE = */ true,
2212 : /*bTakeIntoAccountSpatialFilter = */ true,
2213 : /*bTakeIntoAccountGeomField = */ true);
2214 : }
2215 : }
2216 : }
2217 :
2218 115 : if (!papszLayers && !bIsSummaryCli && !bExportOgrSchema)
2219 : {
2220 91 : ReportRelationships(osRet, oRoot, psOptions, poDS);
2221 : }
2222 :
2223 115 : if (bJson)
2224 : {
2225 53 : osRet.clear();
2226 53 : ConcatStr(
2227 53 : osRet, psOptions->bStdoutOutput,
2228 : json_object_to_json_string_ext(
2229 53 : static_cast<struct json_object *>(oRoot.GetInternalHandle()),
2230 : JSON_C_TO_STRING_PRETTY
2231 : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
2232 : | JSON_C_TO_STRING_NOSLASHESCAPE
2233 : #endif
2234 : ));
2235 53 : ConcatStr(osRet, psOptions->bStdoutOutput, "\n");
2236 : }
2237 :
2238 115 : return VSI_STRDUP_VERBOSE(osRet);
2239 : }
2240 :
2241 : /************************************************************************/
2242 : /* GDALVectorInfoOptionsGetParser() */
2243 : /************************************************************************/
2244 :
2245 123 : static std::unique_ptr<GDALArgumentParser> GDALVectorInfoOptionsGetParser(
2246 : GDALVectorInfoOptions *psOptions,
2247 : GDALVectorInfoOptionsForBinary *psOptionsForBinary)
2248 : {
2249 : auto argParser = std::make_unique<GDALArgumentParser>(
2250 123 : "ogrinfo", /* bForBinary=*/psOptionsForBinary != nullptr);
2251 :
2252 123 : argParser->add_description(
2253 123 : _("Lists information about an OGR-supported data source."));
2254 :
2255 123 : argParser->add_epilog(
2256 123 : _("For more details, consult https://gdal.org/programs/ogrinfo.html"));
2257 :
2258 123 : argParser->add_argument("-json")
2259 123 : .flag()
2260 44 : .action([psOptions](const std::string &)
2261 123 : { psOptions->eFormat = FORMAT_JSON; })
2262 123 : .help(_("Display the output in json format."));
2263 :
2264 : // Hidden argument to select OGR_SCHEMA output
2265 123 : argParser->add_argument("-schema")
2266 123 : .flag()
2267 123 : .hidden()
2268 10 : .action([psOptions](const std::string &)
2269 123 : { psOptions->bExportOgrSchema = true; })
2270 123 : .help(_("Export the OGR_SCHEMA in json format."));
2271 :
2272 123 : argParser->add_argument("-ro")
2273 123 : .flag()
2274 : .action(
2275 14 : [psOptionsForBinary](const std::string &)
2276 : {
2277 7 : if (psOptionsForBinary)
2278 7 : psOptionsForBinary->bReadOnly = true;
2279 123 : })
2280 123 : .help(_("Open the data source in read-only mode."));
2281 :
2282 123 : argParser->add_argument("-update")
2283 123 : .flag()
2284 : .action(
2285 0 : [psOptionsForBinary](const std::string &)
2286 : {
2287 0 : if (psOptionsForBinary)
2288 0 : psOptionsForBinary->bUpdate = true;
2289 123 : })
2290 123 : .help(_("Open the data source in update mode."));
2291 :
2292 123 : argParser->add_argument("-q", "--quiet")
2293 123 : .flag()
2294 : .action(
2295 4 : [psOptions, psOptionsForBinary](const std::string &)
2296 : {
2297 2 : psOptions->bVerbose = false;
2298 2 : if (psOptionsForBinary)
2299 2 : psOptionsForBinary->bVerbose = false;
2300 123 : })
2301 : .help(_("Quiet mode. No progress message is emitted on the standard "
2302 123 : "output."));
2303 :
2304 : #ifdef __AFL_HAVE_MANUAL_CONTROL
2305 : /* Undocumented: mainly only useful for AFL testing */
2306 : argParser->add_argument("-qq")
2307 : .flag()
2308 : .hidden()
2309 : .action(
2310 : [psOptions, psOptionsForBinary](const std::string &)
2311 : {
2312 : psOptions->bVerbose = false;
2313 : if (psOptionsForBinary)
2314 : psOptionsForBinary->bVerbose = false;
2315 : psOptions->bSuperQuiet = true;
2316 : })
2317 : .help(_("Super quiet mode."));
2318 : #endif
2319 :
2320 123 : argParser->add_argument("-fid")
2321 246 : .metavar("<FID>")
2322 123 : .store_into(psOptions->nFetchFID)
2323 123 : .help(_("Only the feature with this feature id will be reported."));
2324 :
2325 123 : argParser->add_argument("-spat")
2326 246 : .metavar("<xmin> <ymin> <xmax> <ymax>")
2327 123 : .nargs(4)
2328 123 : .scan<'g', double>()
2329 : .help(_("The area of interest. Only features within the rectangle will "
2330 123 : "be reported."));
2331 :
2332 123 : argParser->add_argument("-geomfield")
2333 246 : .metavar("<field>")
2334 123 : .store_into(psOptions->osGeomField)
2335 : .help(_("Name of the geometry field on which the spatial filter "
2336 123 : "operates."));
2337 :
2338 123 : argParser->add_argument("-where")
2339 246 : .metavar("<restricted_where>")
2340 123 : .store_into(psOptions->osWHERE)
2341 : .help(_("An attribute query in a restricted form of the queries used "
2342 123 : "in the SQL WHERE statement."));
2343 :
2344 : {
2345 123 : auto &group = argParser->add_mutually_exclusive_group();
2346 123 : group.add_argument("-sql")
2347 246 : .metavar("<statement|@filename>")
2348 123 : .store_into(psOptions->osSQLStatement)
2349 : .help(_(
2350 123 : "Execute the indicated SQL statement and return the result."));
2351 :
2352 123 : group.add_argument("-rl")
2353 123 : .store_into(psOptions->bDatasetGetNextFeature)
2354 123 : .help(_("Enable random layer reading mode."));
2355 : }
2356 :
2357 123 : argParser->add_argument("-dialect")
2358 246 : .metavar("<dialect>")
2359 123 : .store_into(psOptions->osDialect)
2360 123 : .help(_("SQL dialect."));
2361 :
2362 : // Only for fuzzing
2363 123 : argParser->add_argument("-rc")
2364 123 : .hidden()
2365 246 : .metavar("<count>")
2366 123 : .store_into(psOptions->nRepeatCount)
2367 123 : .help(_("Repeat count"));
2368 :
2369 123 : argParser->add_argument("-al")
2370 123 : .store_into(psOptions->bAllLayers)
2371 : .help(_("List all layers (used instead of having to give layer names "
2372 123 : "as arguments)."));
2373 :
2374 : {
2375 123 : auto &group = argParser->add_mutually_exclusive_group();
2376 123 : group.add_argument("-so", "-summary")
2377 123 : .store_into(psOptions->bSummaryUserRequested)
2378 : .help(_("Summary only: show only summary information like "
2379 123 : "projection, schema, feature count and extents."));
2380 :
2381 123 : group.add_argument("-features")
2382 123 : .store_into(psOptions->bFeaturesUserRequested)
2383 123 : .help(_("Enable listing of features."));
2384 : }
2385 :
2386 123 : argParser->add_argument("-limit")
2387 246 : .metavar("<nb_features>")
2388 123 : .store_into(psOptions->nLimit)
2389 123 : .help(_("Limit the number of features per layer."));
2390 :
2391 123 : argParser->add_argument("-fields")
2392 123 : .choices("YES", "NO")
2393 246 : .metavar("YES|NO")
2394 : .action(
2395 2 : [psOptions](const std::string &s)
2396 : {
2397 2 : psOptions->aosOptions.SetNameValue("DISPLAY_FIELDS", s.c_str());
2398 123 : })
2399 : .help(
2400 123 : _("If set to NO, the feature dump will not display field values."));
2401 :
2402 123 : argParser->add_argument("-geom")
2403 123 : .choices("YES", "NO", "SUMMARY", "WKT", "ISO_WKT")
2404 246 : .metavar("YES|NO|SUMMARY|WKT|ISO_WKT")
2405 : .action(
2406 3 : [psOptions](const std::string &s)
2407 : {
2408 : psOptions->aosOptions.SetNameValue("DISPLAY_GEOMETRY",
2409 3 : s.c_str());
2410 123 : })
2411 123 : .help(_("How to display geometries in feature dump."));
2412 :
2413 123 : argParser->add_argument("-oo")
2414 123 : .append()
2415 246 : .metavar("<NAME=VALUE>")
2416 : .action(
2417 20 : [psOptionsForBinary](const std::string &s)
2418 : {
2419 10 : if (psOptionsForBinary)
2420 10 : psOptionsForBinary->aosOpenOptions.AddString(s.c_str());
2421 123 : })
2422 123 : .help(_("Dataset open option (format-specific)."));
2423 :
2424 123 : argParser->add_argument("-nomd")
2425 123 : .flag()
2426 1 : .action([psOptions](const std::string &)
2427 123 : { psOptions->bShowMetadata = false; })
2428 123 : .help(_("Suppress metadata printing."));
2429 :
2430 123 : argParser->add_argument("-listmdd")
2431 123 : .store_into(psOptions->bListMDD)
2432 123 : .help(_("List all metadata domains available for the dataset."));
2433 :
2434 123 : argParser->add_argument("-mdd")
2435 123 : .append()
2436 246 : .metavar("<domain>")
2437 1 : .action([psOptions](const std::string &s)
2438 124 : { psOptions->aosExtraMDDomains.AddString(s.c_str()); })
2439 123 : .help(_("List metadata in the specified domain."));
2440 :
2441 123 : argParser->add_argument("-nocount")
2442 123 : .flag()
2443 2 : .action([psOptions](const std::string &)
2444 123 : { psOptions->bFeatureCount = false; })
2445 123 : .help(_("Suppress feature count printing."));
2446 :
2447 123 : argParser->add_argument("-noextent")
2448 123 : .flag()
2449 0 : .action([psOptions](const std::string &)
2450 123 : { psOptions->bExtent = false; })
2451 123 : .help(_("Suppress spatial extent printing."));
2452 :
2453 123 : argParser->add_argument("-extent3D")
2454 123 : .store_into(psOptions->bExtent3D)
2455 123 : .help(_("Request a 3D extent to be reported."));
2456 :
2457 123 : argParser->add_argument("-nogeomtype")
2458 123 : .flag()
2459 1 : .action([psOptions](const std::string &)
2460 123 : { psOptions->bGeomType = false; })
2461 123 : .help(_("Suppress layer geometry type printing."));
2462 :
2463 123 : argParser->add_argument("-wkt_format")
2464 123 : .store_into(psOptions->osWKTFormat)
2465 246 : .metavar("WKT1|WKT2|WKT2_2015|WKT2_2019")
2466 123 : .help(_("The WKT format used to display the SRS."));
2467 :
2468 123 : argParser->add_argument("-fielddomain")
2469 123 : .store_into(psOptions->osFieldDomain)
2470 246 : .metavar("<name>")
2471 123 : .help(_("Display details about a field domain."));
2472 :
2473 123 : argParser->add_argument("-if")
2474 123 : .append()
2475 246 : .metavar("<format>")
2476 : .action(
2477 4 : [psOptionsForBinary](const std::string &s)
2478 : {
2479 2 : if (psOptionsForBinary)
2480 : {
2481 2 : if (GDALGetDriverByName(s.c_str()) == nullptr)
2482 : {
2483 0 : CPLError(CE_Warning, CPLE_AppDefined,
2484 : "%s is not a recognized driver", s.c_str());
2485 : }
2486 : psOptionsForBinary->aosAllowInputDrivers.AddString(
2487 2 : s.c_str());
2488 : }
2489 123 : })
2490 123 : .help(_("Format/driver name(s) to try when opening the input file."));
2491 :
2492 123 : argParser->add_argument("-stdout")
2493 123 : .flag()
2494 123 : .store_into(psOptions->bStdoutOutput)
2495 123 : .hidden()
2496 123 : .help(_("Directly output on stdout (format=text mode only)"));
2497 :
2498 123 : argParser->add_argument("--cli")
2499 123 : .hidden()
2500 123 : .store_into(psOptions->bIsCli)
2501 : .help(_("Indicates that this is called from the gdal vector info CLI "
2502 123 : "utility."));
2503 :
2504 123 : auto &argFilename = argParser->add_argument("filename")
2505 : .action(
2506 130 : [psOptionsForBinary](const std::string &s)
2507 : {
2508 87 : if (psOptionsForBinary)
2509 43 : psOptionsForBinary->osFilename = s;
2510 123 : })
2511 123 : .help(_("The data source to open."));
2512 123 : if (!psOptionsForBinary)
2513 78 : argFilename.nargs(argparse::nargs_pattern::optional);
2514 :
2515 123 : argParser->add_argument("layer")
2516 123 : .remaining()
2517 246 : .metavar("<layer_name>")
2518 123 : .help(_("Layer name."));
2519 :
2520 123 : return argParser;
2521 : }
2522 :
2523 : /************************************************************************/
2524 : /* GDALVectorInfoGetParserUsage() */
2525 : /************************************************************************/
2526 :
2527 1 : std::string GDALVectorInfoGetParserUsage()
2528 : {
2529 : try
2530 : {
2531 2 : GDALVectorInfoOptions sOptions;
2532 2 : GDALVectorInfoOptionsForBinary sOptionsForBinary;
2533 : auto argParser =
2534 2 : GDALVectorInfoOptionsGetParser(&sOptions, &sOptionsForBinary);
2535 1 : return argParser->usage();
2536 : }
2537 0 : catch (const std::exception &err)
2538 : {
2539 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
2540 0 : err.what());
2541 0 : return std::string();
2542 : }
2543 : }
2544 :
2545 : /************************************************************************/
2546 : /* GDALVectorInfoOptionsNew() */
2547 : /************************************************************************/
2548 :
2549 : /**
2550 : * Allocates a GDALVectorInfoOptions struct.
2551 : *
2552 : * Note that when this function is used a library function, and not from the
2553 : * ogrinfo utility, a dataset name must be specified if any layer names(s) are
2554 : * specified (if no layer name is specific, passing a dataset name is not
2555 : * needed). That dataset name may be a dummy one, as the dataset taken into
2556 : * account is the hDS parameter passed to GDALVectorInfo().
2557 : * Similarly the -oo switch in a non-ogrinfo context will be ignored, and it
2558 : * is the responsibility of the user to apply them when opening the hDS parameter
2559 : * passed to GDALVectorInfo().
2560 : *
2561 : * @param papszArgv NULL terminated list of options (potentially including
2562 : * filename and open options too), or NULL. The accepted options are the ones of
2563 : * the <a href="/programs/ogrinfo.html">ogrinfo</a> utility.
2564 : * @param psOptionsForBinary (output) may be NULL (and should generally be
2565 : * NULL), otherwise (ogrinfo_bin.cpp use case) must be allocated with
2566 : * GDALVectorInfoOptionsForBinaryNew() prior to this
2567 : * function. Will be filled with potentially present filename, open options,
2568 : * subdataset number...
2569 : * @return pointer to the allocated GDALVectorInfoOptions struct. Must be freed
2570 : * with GDALVectorInfoOptionsFree().
2571 : *
2572 : * @since GDAL 3.7
2573 : */
2574 :
2575 : GDALVectorInfoOptions *
2576 122 : GDALVectorInfoOptionsNew(char **papszArgv,
2577 : GDALVectorInfoOptionsForBinary *psOptionsForBinary)
2578 : {
2579 244 : auto psOptions = std::make_unique<GDALVectorInfoOptions>();
2580 :
2581 : try
2582 : {
2583 : auto argParser =
2584 244 : GDALVectorInfoOptionsGetParser(psOptions.get(), psOptionsForBinary);
2585 :
2586 : /* Special pre-processing to rewrite -fields=foo as "-fields" "FOO", and
2587 : * same for -geom=foo. */
2588 244 : CPLStringList aosArgv;
2589 535 : for (CSLConstList papszIter = papszArgv; papszIter && *papszIter;
2590 : ++papszIter)
2591 : {
2592 413 : if (STARTS_WITH(*papszIter, "-fields="))
2593 : {
2594 2 : aosArgv.AddString("-fields");
2595 : aosArgv.AddString(
2596 2 : CPLString(*papszIter + strlen("-fields=")).toupper());
2597 : }
2598 411 : else if (STARTS_WITH(*papszIter, "-geom="))
2599 : {
2600 3 : aosArgv.AddString("-geom");
2601 : aosArgv.AddString(
2602 3 : CPLString(*papszIter + strlen("-geom=")).toupper());
2603 : }
2604 : else
2605 : {
2606 408 : aosArgv.AddString(*papszIter);
2607 : }
2608 : }
2609 :
2610 122 : argParser->parse_args_without_binary_name(aosArgv.List());
2611 :
2612 242 : auto layers = argParser->present<std::vector<std::string>>("layer");
2613 121 : if (layers)
2614 : {
2615 23 : for (const auto &layer : *layers)
2616 : {
2617 12 : psOptions->aosLayers.AddString(layer.c_str());
2618 12 : psOptions->bAllLayers = false;
2619 : }
2620 : }
2621 :
2622 123 : if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
2623 : {
2624 2 : const double dfMinX = (*oSpat)[0];
2625 2 : const double dfMinY = (*oSpat)[1];
2626 2 : const double dfMaxX = (*oSpat)[2];
2627 2 : const double dfMaxY = (*oSpat)[3];
2628 :
2629 : auto poPolygon =
2630 4 : std::make_unique<OGRPolygon>(dfMinX, dfMinY, dfMaxX, dfMaxY);
2631 2 : psOptions->poSpatialFilter.reset(poPolygon.release());
2632 : }
2633 :
2634 121 : if (!psOptions->osWHERE.empty() && psOptions->osWHERE[0] == '@')
2635 : {
2636 0 : GByte *pabyRet = nullptr;
2637 0 : if (VSIIngestFile(nullptr, psOptions->osWHERE.substr(1).c_str(),
2638 0 : &pabyRet, nullptr, 10 * 1024 * 1024))
2639 : {
2640 0 : GDALRemoveBOM(pabyRet);
2641 0 : psOptions->osWHERE = reinterpret_cast<const char *>(pabyRet);
2642 0 : VSIFree(pabyRet);
2643 : }
2644 : else
2645 : {
2646 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2647 0 : psOptions->osWHERE.substr(1).c_str());
2648 0 : return nullptr;
2649 : }
2650 : }
2651 :
2652 126 : if (!psOptions->osSQLStatement.empty() &&
2653 5 : psOptions->osSQLStatement[0] == '@')
2654 : {
2655 1 : GByte *pabyRet = nullptr;
2656 1 : if (VSIIngestFile(nullptr,
2657 2 : psOptions->osSQLStatement.substr(1).c_str(),
2658 1 : &pabyRet, nullptr, 10 * 1024 * 1024))
2659 : {
2660 1 : GDALRemoveBOM(pabyRet);
2661 1 : char *pszSQLStatement = reinterpret_cast<char *>(pabyRet);
2662 1 : psOptions->osSQLStatement =
2663 2 : CPLRemoveSQLComments(pszSQLStatement);
2664 1 : VSIFree(pabyRet);
2665 : }
2666 : else
2667 : {
2668 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2669 0 : psOptions->osSQLStatement.substr(1).c_str());
2670 0 : return nullptr;
2671 : }
2672 : }
2673 :
2674 121 : if (psOptionsForBinary)
2675 : {
2676 43 : psOptions->bStdoutOutput = true;
2677 43 : psOptionsForBinary->osSQLStatement = psOptions->osSQLStatement;
2678 : }
2679 :
2680 121 : if (psOptions->eFormat == FORMAT_JSON)
2681 : {
2682 44 : psOptions->bAllLayers = true;
2683 44 : psOptions->bSummaryOnly = true;
2684 44 : if (psOptions->aosExtraMDDomains.empty())
2685 44 : psOptions->aosExtraMDDomains.AddString("all");
2686 44 : psOptions->bStdoutOutput = false;
2687 : }
2688 :
2689 121 : if (psOptions->bSummaryUserRequested)
2690 17 : psOptions->bSummaryOnly = true;
2691 104 : else if (psOptions->bFeaturesUserRequested)
2692 13 : psOptions->bSummaryOnly = false;
2693 :
2694 121 : if (!psOptions->osDialect.empty() && !psOptions->osWHERE.empty() &&
2695 0 : psOptions->osSQLStatement.empty())
2696 : {
2697 0 : CPLError(CE_Warning, CPLE_AppDefined,
2698 : "-dialect is ignored with -where. Use -sql instead");
2699 : }
2700 :
2701 : // Patch options when -schema is set
2702 121 : if (psOptions->bExportOgrSchema)
2703 : {
2704 : // TODO: validate and raise an error if incompatible options are set?
2705 : // not strictly necessary given that -schema is an hidden option
2706 10 : psOptions->eFormat = FORMAT_JSON;
2707 10 : psOptions->bAllLayers = true;
2708 10 : psOptions->bShowMetadata = false;
2709 10 : psOptions->bListMDD = false;
2710 10 : psOptions->bFeatureCount = false;
2711 10 : psOptions->bIsCli = true;
2712 10 : psOptions->bSummaryOnly = false;
2713 10 : psOptions->bExtent = false;
2714 10 : psOptions->bExtent3D = false;
2715 : }
2716 :
2717 121 : return psOptions.release();
2718 : }
2719 1 : catch (const std::exception &err)
2720 : {
2721 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
2722 1 : return nullptr;
2723 : }
2724 : }
|