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