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 :
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 107 : void GDALVectorInfoOptionsFree(GDALVectorInfoOptions *psOptions)
89 : {
90 107 : delete psOptions;
91 107 : }
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 2862 : static void Concat(CPLString &osRet, bool bStdoutOutput, const char *pszFormat,
103 : ...)
104 : {
105 : va_list args;
106 2862 : va_start(args, pszFormat);
107 :
108 2862 : if (bStdoutOutput)
109 : {
110 2211 : vfprintf(stdout, pszFormat, args);
111 : }
112 : else
113 : {
114 : try
115 : {
116 1302 : CPLString osTarget;
117 651 : osTarget.vPrintf(pszFormat, args);
118 :
119 651 : 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 2862 : va_end(args);
128 2862 : }
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.5f));
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.5f));
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 87 : static void ReportRelationships(CPLString &osRet, CPLJSONObject &oRoot,
460 : const GDALVectorInfoOptions *psOptions,
461 : const GDALDataset *poDS)
462 : {
463 87 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
464 174 : CPLJSONObject oRelationships;
465 87 : if (bJson)
466 35 : oRoot.Add("relationships", oRelationships);
467 :
468 174 : const auto aosRelationshipNames = poDS->GetRelationshipNames();
469 109 : 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 87 : }
628 :
629 : /************************************************************************/
630 : /* GDALVectorInfoPrintMetadata() */
631 : /************************************************************************/
632 :
633 : static void
634 538 : GDALVectorInfoPrintMetadata(CPLString &osRet, CPLJSONObject &oMetadata,
635 : const GDALVectorInfoOptions *psOptions,
636 : GDALMajorObjectH hObject, const char *pszDomain,
637 : const char *pszDisplayedname, const char *pszIndent)
638 : {
639 538 : const bool bJsonOutput = psOptions->eFormat == FORMAT_JSON;
640 538 : bool bIsxml = false;
641 538 : bool bMDIsJson = false;
642 :
643 538 : if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "xml:"))
644 0 : bIsxml = true;
645 538 : else if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "json:"))
646 1 : bMDIsJson = true;
647 :
648 538 : CSLConstList papszMetadata = GDALGetMetadata(hObject, pszDomain);
649 538 : if (CSLCount(papszMetadata) > 0)
650 : {
651 46 : CPLJSONObject oMetadataDomain;
652 46 : if (!bJsonOutput)
653 19 : 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 41 : if (bIsxml)
660 : {
661 0 : oMetadata.Add(pszDomain, papszMetadata[i]);
662 0 : return;
663 : }
664 41 : 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 40 : char *pszKey = nullptr;
674 : const char *pszValue =
675 40 : CPLParseNameValue(papszMetadata[i], &pszKey);
676 40 : if (pszKey)
677 : {
678 40 : oMetadataDomain.Add(pszKey, pszValue);
679 40 : CPLFree(pszKey);
680 : }
681 : }
682 : }
683 19 : else if (bIsxml)
684 0 : Concat(osRet, psOptions->bStdoutOutput, "%s%s\n", pszIndent,
685 0 : papszMetadata[i]);
686 : else
687 19 : Concat(osRet, psOptions->bStdoutOutput, "%s %s\n", pszIndent,
688 19 : papszMetadata[i]);
689 : }
690 45 : if (bJsonOutput)
691 : {
692 26 : oMetadata.Add(pszDomain ? pszDomain : "", oMetadataDomain);
693 : }
694 : }
695 : }
696 :
697 : /************************************************************************/
698 : /* GDALVectorInfoReportMetadata() */
699 : /************************************************************************/
700 :
701 261 : static void GDALVectorInfoReportMetadata(CPLString &osRet, CPLJSONObject &oRoot,
702 : const GDALVectorInfoOptions *psOptions,
703 : GDALMajorObject *poMajorObject,
704 : bool bListMDD, bool bShowMetadata,
705 : CSLConstList papszExtraMDDomains)
706 : {
707 261 : const char *pszIndent = "";
708 261 : auto hObject = GDALMajorObject::ToHandle(poMajorObject);
709 :
710 261 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
711 : /* -------------------------------------------------------------------- */
712 : /* Report list of Metadata domains */
713 : /* -------------------------------------------------------------------- */
714 261 : 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 261 : if (!bShowMetadata)
748 2 : return;
749 :
750 : /* -------------------------------------------------------------------- */
751 : /* Report default Metadata domain. */
752 : /* -------------------------------------------------------------------- */
753 518 : CPLJSONObject oMetadata;
754 259 : oRoot.Add("metadata", oMetadata);
755 259 : GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject, nullptr,
756 : "Metadata", pszIndent);
757 :
758 : /* -------------------------------------------------------------------- */
759 : /* Report extra Metadata domains */
760 : /* -------------------------------------------------------------------- */
761 259 : if (papszExtraMDDomains != nullptr)
762 : {
763 182 : CPLStringList aosExtraMDDomainsExpanded;
764 :
765 91 : if (EQUAL(papszExtraMDDomains[0], "all") &&
766 91 : papszExtraMDDomains[1] == nullptr)
767 : {
768 182 : const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject));
769 123 : for (const char *pszDomain : aosMDDList)
770 : {
771 32 : if (!EQUAL(pszDomain, "") && !EQUAL(pszDomain, "SUBDATASETS"))
772 : {
773 20 : aosExtraMDDomainsExpanded.AddString(pszDomain);
774 : }
775 91 : }
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 60 : std::string("Metadata (").append(pszDomain).append(")");
786 20 : GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject,
787 : pszDomain, osDisplayedName.c_str(),
788 : pszIndent);
789 : }
790 : }
791 259 : GDALVectorInfoPrintMetadata(osRet, oMetadata, psOptions, hObject,
792 : "SUBDATASETS", "Subdatasets", pszIndent);
793 : }
794 :
795 : /************************************************************************/
796 : /* ReportOnLayer() */
797 : /************************************************************************/
798 :
799 162 : 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 162 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
807 162 : const bool bIsSummaryCli =
808 162 : psOptions->bIsCli && psOptions->bSummaryUserRequested;
809 162 : OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
810 :
811 162 : oLayer.Set("name", poLayer->GetName());
812 : const int nGeomFieldCount =
813 162 : psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
814 :
815 : /* -------------------------------------------------------------------- */
816 : /* Set filters if provided. */
817 : /* -------------------------------------------------------------------- */
818 162 : if (bTakeIntoAccountWHERE && !psOptions->osWHERE.empty())
819 : {
820 4 : if (poLayer->SetAttributeFilter(psOptions->osWHERE.c_str()) !=
821 : OGRERR_NONE)
822 : {
823 0 : CPLError(CE_Failure, CPLE_AppDefined,
824 : "SetAttributeFilter(%s) failed.",
825 0 : psOptions->osWHERE.c_str());
826 0 : return;
827 : }
828 : }
829 :
830 162 : if (bTakeIntoAccountSpatialFilter && psOptions->poSpatialFilter != nullptr)
831 : {
832 2 : if (bTakeIntoAccountGeomField && !psOptions->osGeomField.empty())
833 : {
834 : const int iGeomField =
835 1 : poDefn->GetGeomFieldIndex(psOptions->osGeomField.c_str());
836 1 : if (iGeomField >= 0)
837 1 : poLayer->SetSpatialFilter(iGeomField,
838 1 : psOptions->poSpatialFilter.get());
839 : else
840 0 : CPLError(CE_Warning, CPLE_AppDefined,
841 : "Cannot find geometry field %s.",
842 0 : psOptions->osGeomField.c_str());
843 : }
844 : else
845 : {
846 1 : poLayer->SetSpatialFilter(psOptions->poSpatialFilter.get());
847 : }
848 : }
849 :
850 : /* -------------------------------------------------------------------- */
851 : /* Report various overall information. */
852 : /* -------------------------------------------------------------------- */
853 162 : if (!bJson && !psOptions->bSuperQuiet)
854 : {
855 112 : Concat(osRet, psOptions->bStdoutOutput, "\n");
856 112 : Concat(osRet, psOptions->bStdoutOutput, "Layer name: %s\n",
857 112 : poLayer->GetName());
858 : }
859 :
860 162 : GDALVectorInfoReportMetadata(osRet, oLayer, psOptions, poLayer,
861 162 : !bIsSummaryCli && psOptions->bListMDD,
862 162 : !bIsSummaryCli && psOptions->bShowMetadata,
863 162 : psOptions->aosExtraMDDomains.List());
864 :
865 162 : if (psOptions->bVerbose)
866 : {
867 :
868 322 : CPLString osWKTFormat("FORMAT=");
869 161 : osWKTFormat += psOptions->osWKTFormat;
870 161 : const char *const apszWKTOptions[] = {osWKTFormat.c_str(),
871 161 : "MULTILINE=YES", nullptr};
872 :
873 161 : if (bJson || nGeomFieldCount > 1)
874 : {
875 104 : CPLJSONArray oGeometryFields;
876 52 : if (bJson)
877 50 : oLayer.Add("geometryFields", oGeometryFields);
878 93 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
879 : {
880 : const OGRGeomFieldDefn *poGFldDefn =
881 41 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
882 41 : if (bJson)
883 : {
884 74 : CPLJSONObject oGeometryField;
885 37 : oGeometryFields.Add(oGeometryField);
886 37 : oGeometryField.Set("name", poGFldDefn->GetNameRef());
887 37 : oGeometryField.Set(
888 37 : "type", OGRToOGCGeomType(poGFldDefn->GetType(),
889 : /*bCamelCase=*/true,
890 : /*bAddZm=*/true,
891 : /*bSpaceBeforeZM=*/false));
892 37 : oGeometryField.Set("nullable",
893 37 : CPL_TO_BOOL(poGFldDefn->IsNullable()));
894 37 : if (psOptions->bExtent3D)
895 : {
896 3 : OGREnvelope3D oExt;
897 3 : if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) ==
898 : OGRERR_NONE)
899 : {
900 : {
901 3 : CPLJSONArray oBbox;
902 3 : oBbox.Add(oExt.MinX);
903 3 : oBbox.Add(oExt.MinY);
904 3 : oBbox.Add(oExt.MaxX);
905 3 : oBbox.Add(oExt.MaxY);
906 3 : oGeometryField.Add("extent", oBbox);
907 : }
908 : {
909 3 : CPLJSONArray oBbox;
910 3 : oBbox.Add(oExt.MinX);
911 3 : oBbox.Add(oExt.MinY);
912 3 : if (std::isfinite(oExt.MinZ))
913 2 : oBbox.Add(oExt.MinZ);
914 : else
915 1 : oBbox.AddNull();
916 3 : oBbox.Add(oExt.MaxX);
917 3 : oBbox.Add(oExt.MaxY);
918 3 : if (std::isfinite(oExt.MaxZ))
919 2 : oBbox.Add(oExt.MaxZ);
920 : else
921 1 : oBbox.AddNull();
922 3 : oGeometryField.Add("extent3D", oBbox);
923 : }
924 : }
925 : }
926 34 : else if (psOptions->bExtent)
927 : {
928 34 : OGREnvelope oExt;
929 34 : if (poLayer->GetExtent(iGeom, &oExt, TRUE) ==
930 : OGRERR_NONE)
931 : {
932 24 : CPLJSONArray oBbox;
933 24 : oBbox.Add(oExt.MinX);
934 24 : oBbox.Add(oExt.MinY);
935 24 : oBbox.Add(oExt.MaxX);
936 24 : oBbox.Add(oExt.MaxY);
937 24 : oGeometryField.Add("extent", oBbox);
938 : }
939 : }
940 : const OGRSpatialReference *poSRS =
941 37 : poGFldDefn->GetSpatialRef();
942 37 : if (poSRS)
943 : {
944 58 : CPLJSONObject oCRS;
945 29 : oGeometryField.Add("coordinateSystem", oCRS);
946 29 : char *pszWKT = nullptr;
947 29 : poSRS->exportToWkt(&pszWKT, apszWKTOptions);
948 29 : if (pszWKT)
949 : {
950 29 : oCRS.Set("wkt", pszWKT);
951 29 : CPLFree(pszWKT);
952 : }
953 :
954 : {
955 29 : char *pszProjJson = nullptr;
956 : // PROJJSON requires PROJ >= 6.2
957 : CPLErrorStateBackuper oCPLErrorHandlerPusher(
958 58 : CPLQuietErrorHandler);
959 29 : CPL_IGNORE_RET_VAL(
960 29 : poSRS->exportToPROJJSON(&pszProjJson, nullptr));
961 29 : if (pszProjJson)
962 : {
963 58 : CPLJSONDocument oDoc;
964 29 : if (oDoc.LoadMemory(pszProjJson))
965 : {
966 29 : oCRS.Add("projjson", oDoc.GetRoot());
967 : }
968 29 : CPLFree(pszProjJson);
969 : }
970 : }
971 :
972 : const auto &anAxes =
973 29 : poSRS->GetDataAxisToSRSAxisMapping();
974 58 : CPLJSONArray oAxisMapping;
975 88 : for (const auto nAxis : anAxes)
976 : {
977 59 : oAxisMapping.Add(nAxis);
978 : }
979 29 : oCRS.Add("dataAxisToSRSAxisMapping", oAxisMapping);
980 :
981 : const double dfCoordinateEpoch =
982 29 : poSRS->GetCoordinateEpoch();
983 29 : if (dfCoordinateEpoch > 0)
984 2 : oCRS.Set("coordinateEpoch", dfCoordinateEpoch);
985 : }
986 : else
987 : {
988 8 : oGeometryField.SetNull("coordinateSystem");
989 : }
990 :
991 37 : const auto &srsList = poLayer->GetSupportedSRSList(iGeom);
992 37 : if (!srsList.empty())
993 : {
994 1 : CPLJSONArray oSupportedSRSList;
995 3 : for (const auto &poSupportedSRS : srsList)
996 : {
997 : const char *pszAuthName =
998 2 : poSupportedSRS->GetAuthorityName(nullptr);
999 : const char *pszAuthCode =
1000 2 : poSupportedSRS->GetAuthorityCode(nullptr);
1001 4 : CPLJSONObject oSupportedSRS;
1002 2 : if (pszAuthName && pszAuthCode)
1003 : {
1004 4 : CPLJSONObject id;
1005 2 : id.Set("authority", pszAuthName);
1006 2 : id.Set("code", pszAuthCode);
1007 2 : oSupportedSRS.Add("id", id);
1008 4 : oSupportedSRSList.Add(oSupportedSRS);
1009 : }
1010 : else
1011 : {
1012 0 : char *pszWKT = nullptr;
1013 0 : poSupportedSRS->exportToWkt(&pszWKT,
1014 : apszWKTOptions);
1015 0 : if (pszWKT)
1016 : {
1017 0 : oSupportedSRS.Add("wkt", pszWKT);
1018 0 : oSupportedSRSList.Add(oSupportedSRS);
1019 : }
1020 0 : CPLFree(pszWKT);
1021 : }
1022 : }
1023 1 : oGeometryField.Add("supportedSRSList",
1024 : oSupportedSRSList);
1025 : }
1026 :
1027 : const auto &oCoordPrec =
1028 37 : poGFldDefn->GetCoordinatePrecision();
1029 37 : if (oCoordPrec.dfXYResolution !=
1030 : OGRGeomCoordinatePrecision::UNKNOWN)
1031 : {
1032 3 : oGeometryField.Add("xyCoordinateResolution",
1033 3 : oCoordPrec.dfXYResolution);
1034 : }
1035 37 : if (oCoordPrec.dfZResolution !=
1036 : OGRGeomCoordinatePrecision::UNKNOWN)
1037 : {
1038 3 : oGeometryField.Add("zCoordinateResolution",
1039 3 : oCoordPrec.dfZResolution);
1040 : }
1041 37 : if (oCoordPrec.dfMResolution !=
1042 : OGRGeomCoordinatePrecision::UNKNOWN)
1043 : {
1044 2 : oGeometryField.Add("mCoordinateResolution",
1045 2 : oCoordPrec.dfMResolution);
1046 : }
1047 :
1048 : // For example set by OpenFileGDB driver
1049 37 : if (!oCoordPrec.oFormatSpecificOptions.empty())
1050 : {
1051 2 : CPLJSONObject oFormatSpecificOptions;
1052 2 : for (const auto &formatOptionsPair :
1053 6 : oCoordPrec.oFormatSpecificOptions)
1054 : {
1055 4 : CPLJSONObject oThisFormatSpecificOptions;
1056 44 : for (const auto &[pszKey, pszValue] :
1057 : cpl::IterateNameValue(
1058 46 : formatOptionsPair.second))
1059 : {
1060 : const auto eValueType =
1061 22 : CPLGetValueType(pszValue);
1062 22 : if (eValueType == CPL_VALUE_INTEGER)
1063 : {
1064 14 : oThisFormatSpecificOptions.Add(
1065 : pszKey, CPLAtoGIntBig(pszValue));
1066 : }
1067 8 : else if (eValueType == CPL_VALUE_REAL)
1068 : {
1069 6 : oThisFormatSpecificOptions.Add(
1070 : pszKey, CPLAtof(pszValue));
1071 : }
1072 : else
1073 : {
1074 2 : oThisFormatSpecificOptions.Add(pszKey,
1075 : pszValue);
1076 : }
1077 : }
1078 2 : oFormatSpecificOptions.Add(
1079 2 : formatOptionsPair.first,
1080 : oThisFormatSpecificOptions);
1081 : }
1082 2 : oGeometryField.Add(
1083 : "coordinatePrecisionFormatSpecificOptions",
1084 : oFormatSpecificOptions);
1085 : }
1086 : }
1087 : else
1088 : {
1089 4 : Concat(osRet, psOptions->bStdoutOutput,
1090 : "Geometry (%s): %s\n", poGFldDefn->GetNameRef(),
1091 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1092 : }
1093 52 : }
1094 : }
1095 109 : else if (psOptions->bGeomType)
1096 : {
1097 109 : Concat(osRet, psOptions->bStdoutOutput, "Geometry: %s\n",
1098 109 : OGRGeometryTypeToName(poLayer->GetGeomType()));
1099 : }
1100 :
1101 161 : if (psOptions->bFeatureCount)
1102 : {
1103 159 : if (bJson)
1104 49 : oLayer.Set("featureCount", poLayer->GetFeatureCount());
1105 : else
1106 : {
1107 110 : Concat(osRet, psOptions->bStdoutOutput,
1108 : "Feature Count: " CPL_FRMT_GIB "\n",
1109 110 : poLayer->GetFeatureCount());
1110 : }
1111 : }
1112 :
1113 161 : if (!bJson && psOptions->bExtent && nGeomFieldCount > 1)
1114 : {
1115 6 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1116 : {
1117 4 : if (psOptions->bExtent3D)
1118 : {
1119 0 : OGREnvelope3D oExt;
1120 0 : if (poLayer->GetExtent3D(iGeom, &oExt, TRUE) == OGRERR_NONE)
1121 : {
1122 : OGRGeomFieldDefn *poGFldDefn =
1123 0 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1124 0 : Concat(osRet, psOptions->bStdoutOutput,
1125 : "Extent (%s): (%f, %f, %s) - (%f, %f, %s)\n",
1126 : poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
1127 0 : std::isfinite(oExt.MinZ)
1128 0 : ? CPLSPrintf("%f", oExt.MinZ)
1129 : : "none",
1130 : oExt.MaxX, oExt.MaxY,
1131 0 : std::isfinite(oExt.MaxZ)
1132 0 : ? CPLSPrintf("%f", oExt.MaxZ)
1133 : : "none");
1134 : }
1135 : }
1136 : else
1137 : {
1138 4 : OGREnvelope oExt;
1139 4 : if (poLayer->GetExtent(iGeom, &oExt, TRUE) == OGRERR_NONE)
1140 : {
1141 : OGRGeomFieldDefn *poGFldDefn =
1142 4 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1143 4 : Concat(osRet, psOptions->bStdoutOutput,
1144 : "Extent (%s): (%f, %f) - (%f, %f)\n",
1145 : poGFldDefn->GetNameRef(), oExt.MinX, oExt.MinY,
1146 : oExt.MaxX, oExt.MaxY);
1147 : }
1148 : }
1149 2 : }
1150 : }
1151 159 : else if (!bJson && psOptions->bExtent)
1152 : {
1153 109 : if (psOptions->bExtent3D)
1154 : {
1155 2 : OGREnvelope3D oExt;
1156 2 : if (poLayer->GetExtent3D(0, &oExt, TRUE) == OGRERR_NONE)
1157 : {
1158 4 : Concat(
1159 2 : osRet, psOptions->bStdoutOutput,
1160 : "Extent: (%f, %f, %s) - (%f, %f, %s)\n", oExt.MinX,
1161 : oExt.MinY,
1162 3 : std::isfinite(oExt.MinZ) ? CPLSPrintf("%f", oExt.MinZ)
1163 : : "none",
1164 : oExt.MaxX, oExt.MaxY,
1165 3 : std::isfinite(oExt.MaxZ) ? CPLSPrintf("%f", oExt.MaxZ)
1166 : : "none");
1167 : }
1168 : }
1169 : else
1170 : {
1171 107 : OGREnvelope oExt;
1172 107 : if (poLayer->GetExtent(&oExt, TRUE) == OGRERR_NONE)
1173 : {
1174 29 : Concat(osRet, psOptions->bStdoutOutput,
1175 : "Extent: (%f, %f) - (%f, %f)\n", oExt.MinX,
1176 : oExt.MinY, oExt.MaxX, oExt.MaxY);
1177 : }
1178 : }
1179 : }
1180 :
1181 : const auto displayExtraInfoSRS =
1182 238 : [&osRet, &psOptions](const OGRSpatialReference *poSRS)
1183 : {
1184 39 : const double dfCoordinateEpoch = poSRS->GetCoordinateEpoch();
1185 39 : if (dfCoordinateEpoch > 0)
1186 : {
1187 : std::string osCoordinateEpoch =
1188 4 : CPLSPrintf("%f", dfCoordinateEpoch);
1189 2 : const size_t nDotPos = osCoordinateEpoch.find('.');
1190 2 : if (nDotPos != std::string::npos)
1191 : {
1192 22 : while (osCoordinateEpoch.size() > nDotPos + 2 &&
1193 10 : osCoordinateEpoch.back() == '0')
1194 10 : osCoordinateEpoch.pop_back();
1195 : }
1196 2 : Concat(osRet, psOptions->bStdoutOutput,
1197 : "Coordinate epoch: %s\n", osCoordinateEpoch.c_str());
1198 : }
1199 :
1200 39 : const auto &mapping = poSRS->GetDataAxisToSRSAxisMapping();
1201 39 : Concat(osRet, psOptions->bStdoutOutput,
1202 : "Data axis to CRS axis mapping: ");
1203 118 : for (size_t i = 0; i < mapping.size(); i++)
1204 : {
1205 79 : if (i > 0)
1206 : {
1207 40 : Concat(osRet, psOptions->bStdoutOutput, ",");
1208 : }
1209 79 : Concat(osRet, psOptions->bStdoutOutput, "%d", mapping[i]);
1210 : }
1211 39 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1212 39 : };
1213 :
1214 113 : const auto DisplaySupportedCRSList = [&](int iGeomField)
1215 : {
1216 113 : const auto &srsList = poLayer->GetSupportedSRSList(iGeomField);
1217 113 : if (!srsList.empty())
1218 : {
1219 1 : Concat(osRet, psOptions->bStdoutOutput, "Supported SRS: ");
1220 1 : bool bFirst = true;
1221 3 : for (const auto &poSupportedSRS : srsList)
1222 : {
1223 : const char *pszAuthName =
1224 2 : poSupportedSRS->GetAuthorityName(nullptr);
1225 : const char *pszAuthCode =
1226 2 : poSupportedSRS->GetAuthorityCode(nullptr);
1227 2 : if (!bFirst)
1228 1 : Concat(osRet, psOptions->bStdoutOutput, ", ");
1229 2 : bFirst = false;
1230 2 : if (pszAuthName && pszAuthCode)
1231 : {
1232 2 : Concat(osRet, psOptions->bStdoutOutput, "%s:%s",
1233 : pszAuthName, pszAuthCode);
1234 : }
1235 : else
1236 : {
1237 0 : ConcatStr(osRet, psOptions->bStdoutOutput,
1238 : poSupportedSRS->GetName());
1239 : }
1240 : }
1241 1 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1242 : }
1243 113 : };
1244 :
1245 161 : if (!bJson && nGeomFieldCount > 1)
1246 : {
1247 :
1248 6 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1249 : {
1250 : OGRGeomFieldDefn *poGFldDefn =
1251 4 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1252 4 : const OGRSpatialReference *poSRS = poGFldDefn->GetSpatialRef();
1253 4 : char *pszWKT = nullptr;
1254 4 : if (poSRS == nullptr)
1255 : {
1256 0 : pszWKT = CPLStrdup("(unknown)");
1257 : }
1258 : else
1259 : {
1260 4 : poSRS->exportToWkt(&pszWKT, apszWKTOptions);
1261 : }
1262 :
1263 4 : Concat(osRet, psOptions->bStdoutOutput, "SRS WKT (%s):\n%s\n",
1264 : poGFldDefn->GetNameRef(), pszWKT);
1265 4 : CPLFree(pszWKT);
1266 4 : if (poSRS)
1267 : {
1268 4 : displayExtraInfoSRS(poSRS);
1269 : }
1270 4 : DisplaySupportedCRSList(iGeom);
1271 2 : }
1272 : }
1273 159 : else if (!bJson)
1274 : {
1275 109 : char *pszWKT = nullptr;
1276 109 : auto poSRS = poLayer->GetSpatialRef();
1277 109 : if (poSRS == nullptr)
1278 : {
1279 74 : pszWKT = CPLStrdup("(unknown)");
1280 : }
1281 : else
1282 : {
1283 35 : poSRS->exportToWkt(&pszWKT, apszWKTOptions);
1284 : }
1285 :
1286 109 : Concat(osRet, psOptions->bStdoutOutput, "Layer SRS WKT:\n%s\n",
1287 : pszWKT);
1288 109 : CPLFree(pszWKT);
1289 109 : if (poSRS)
1290 : {
1291 35 : displayExtraInfoSRS(poSRS);
1292 : }
1293 109 : DisplaySupportedCRSList(0);
1294 : }
1295 :
1296 161 : const char *pszFIDColumn = poLayer->GetFIDColumn();
1297 161 : if (pszFIDColumn[0] != '\0')
1298 : {
1299 32 : if (bJson)
1300 17 : oLayer.Set("fidColumnName", pszFIDColumn);
1301 : else
1302 : {
1303 15 : Concat(osRet, psOptions->bStdoutOutput, "FID Column = %s\n",
1304 : pszFIDColumn);
1305 : }
1306 : }
1307 :
1308 169 : for (int iGeom = 0; !bJson && iGeom < nGeomFieldCount; iGeom++)
1309 : {
1310 : OGRGeomFieldDefn *poGFldDefn =
1311 39 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1312 70 : if (nGeomFieldCount == 1 && EQUAL(poGFldDefn->GetNameRef(), "") &&
1313 31 : poGFldDefn->IsNullable())
1314 31 : break;
1315 8 : Concat(osRet, psOptions->bStdoutOutput, "Geometry Column ");
1316 8 : if (nGeomFieldCount > 1)
1317 4 : Concat(osRet, psOptions->bStdoutOutput, "%d ", iGeom + 1);
1318 8 : if (!poGFldDefn->IsNullable())
1319 0 : Concat(osRet, psOptions->bStdoutOutput, "NOT NULL ");
1320 8 : Concat(osRet, psOptions->bStdoutOutput, "= %s\n",
1321 : poGFldDefn->GetNameRef());
1322 : }
1323 :
1324 322 : CPLJSONArray oFields;
1325 161 : if (bJson)
1326 50 : oLayer.Add("fields", oFields);
1327 734 : for (int iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++)
1328 : {
1329 573 : const OGRFieldDefn *poField = poDefn->GetFieldDefn(iAttr);
1330 573 : const char *pszAlias = poField->GetAlternativeNameRef();
1331 573 : const std::string &osDomain = poField->GetDomainName();
1332 573 : const std::string &osComment = poField->GetComment();
1333 573 : const auto eType = poField->GetType();
1334 1146 : std::string osTimeZone;
1335 573 : if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
1336 : {
1337 24 : const int nTZFlag = poField->GetTZFlag();
1338 24 : if (nTZFlag == OGR_TZFLAG_LOCALTIME)
1339 : {
1340 1 : osTimeZone = "localtime";
1341 : }
1342 23 : else if (nTZFlag == OGR_TZFLAG_MIXED_TZ)
1343 : {
1344 1 : osTimeZone = "mixed timezones";
1345 : }
1346 22 : else if (nTZFlag == OGR_TZFLAG_UTC)
1347 : {
1348 1 : osTimeZone = "UTC";
1349 : }
1350 21 : else if (nTZFlag > 0)
1351 : {
1352 : char chSign;
1353 2 : const int nOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
1354 2 : int nHours =
1355 : static_cast<int>(nOffset / 60); // Round towards zero.
1356 2 : const int nMinutes = std::abs(nOffset - nHours * 60);
1357 :
1358 2 : if (nOffset < 0)
1359 : {
1360 1 : chSign = '-';
1361 1 : nHours = std::abs(nHours);
1362 : }
1363 : else
1364 : {
1365 1 : chSign = '+';
1366 : }
1367 : osTimeZone =
1368 2 : CPLSPrintf("%c%02d:%02d", chSign, nHours, nMinutes);
1369 : }
1370 : }
1371 :
1372 573 : if (bJson)
1373 : {
1374 176 : CPLJSONObject oField;
1375 88 : oFields.Add(oField);
1376 88 : oField.Set("name", poField->GetNameRef());
1377 88 : oField.Set("type", OGRFieldDefn::GetFieldTypeName(eType));
1378 88 : if (poField->GetSubType() != OFSTNone)
1379 1 : oField.Set("subType", OGRFieldDefn::GetFieldSubTypeName(
1380 : poField->GetSubType()));
1381 88 : if (poField->GetWidth() > 0)
1382 52 : oField.Set("width", poField->GetWidth());
1383 88 : if (poField->GetPrecision() > 0)
1384 9 : oField.Set("precision", poField->GetPrecision());
1385 88 : oField.Set("nullable", CPL_TO_BOOL(poField->IsNullable()));
1386 88 : oField.Set("uniqueConstraint",
1387 88 : CPL_TO_BOOL(poField->IsUnique()));
1388 88 : if (poField->GetDefault() != nullptr)
1389 0 : oField.Set("defaultValue", poField->GetDefault());
1390 88 : if (pszAlias != nullptr && pszAlias[0])
1391 0 : oField.Set("alias", pszAlias);
1392 88 : if (!osDomain.empty())
1393 5 : oField.Set("domainName", osDomain);
1394 88 : if (!osComment.empty())
1395 0 : oField.Set("comment", osComment);
1396 88 : if (!osTimeZone.empty())
1397 5 : oField.Set("timezone", osTimeZone);
1398 : }
1399 : else
1400 : {
1401 : const char *pszType =
1402 485 : (poField->GetSubType() != OFSTNone)
1403 485 : ? CPLSPrintf("%s(%s)",
1404 : OGRFieldDefn::GetFieldTypeName(
1405 : poField->GetType()),
1406 : OGRFieldDefn::GetFieldSubTypeName(
1407 : poField->GetSubType()))
1408 463 : : OGRFieldDefn::GetFieldTypeName(poField->GetType());
1409 485 : Concat(osRet, psOptions->bStdoutOutput, "%s: %s",
1410 : poField->GetNameRef(), pszType);
1411 485 : if (eType == OFTTime || eType == OFTDate ||
1412 : eType == OFTDateTime)
1413 : {
1414 18 : if (!osTimeZone.empty())
1415 0 : Concat(osRet, psOptions->bStdoutOutput, " (%s)",
1416 : osTimeZone.c_str());
1417 : }
1418 : else
1419 : {
1420 467 : Concat(osRet, psOptions->bStdoutOutput, " (%d.%d)",
1421 : poField->GetWidth(), poField->GetPrecision());
1422 : }
1423 485 : if (poField->IsUnique())
1424 0 : Concat(osRet, psOptions->bStdoutOutput, " UNIQUE");
1425 485 : if (!poField->IsNullable())
1426 204 : Concat(osRet, psOptions->bStdoutOutput, " NOT NULL");
1427 485 : if (poField->GetDefault() != nullptr)
1428 8 : Concat(osRet, psOptions->bStdoutOutput, " DEFAULT %s",
1429 : poField->GetDefault());
1430 485 : if (pszAlias != nullptr && pszAlias[0])
1431 0 : Concat(osRet, psOptions->bStdoutOutput,
1432 : ", alternative name=\"%s\"", pszAlias);
1433 485 : if (!osDomain.empty())
1434 5 : Concat(osRet, psOptions->bStdoutOutput, ", domain name=%s",
1435 : osDomain.c_str());
1436 485 : if (!osComment.empty())
1437 0 : Concat(osRet, psOptions->bStdoutOutput, ", comment=%s",
1438 : osComment.c_str());
1439 485 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1440 : }
1441 : }
1442 : }
1443 :
1444 : /* -------------------------------------------------------------------- */
1445 : /* Read, and dump features. */
1446 : /* -------------------------------------------------------------------- */
1447 :
1448 162 : if (psOptions->nFetchFID == OGRNullFID && !bForceSummary &&
1449 161 : ((psOptions->bIsCli && psOptions->bFeaturesUserRequested) ||
1450 157 : (!psOptions->bIsCli && !psOptions->bSummaryOnly)))
1451 : {
1452 88 : if (!psOptions->bSuperQuiet)
1453 : {
1454 176 : CPLJSONArray oFeatures;
1455 : const bool bDisplayFields =
1456 88 : CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
1457 : "DISPLAY_FIELDS", "YES"));
1458 : const int nFields =
1459 88 : bDisplayFields ? poLayer->GetLayerDefn()->GetFieldCount() : 0;
1460 : const bool bDisplayGeometry =
1461 88 : CPLTestBool(psOptions->aosOptions.FetchNameValueDef(
1462 : "DISPLAY_GEOMETRY", "YES"));
1463 : const int nGeomFields =
1464 88 : bDisplayGeometry ? poLayer->GetLayerDefn()->GetGeomFieldCount()
1465 88 : : 0;
1466 88 : if (bJson)
1467 11 : oLayer.Add("features", oFeatures);
1468 88 : GIntBig nFeatureCount = 0;
1469 730 : for (auto &poFeature : poLayer)
1470 : {
1471 642 : if (psOptions->nLimit >= 0 &&
1472 5 : nFeatureCount >= psOptions->nLimit)
1473 : {
1474 2 : break;
1475 : }
1476 640 : ++nFeatureCount;
1477 :
1478 640 : if (bJson)
1479 : {
1480 88 : CPLJSONObject oFeature;
1481 88 : CPLJSONObject oProperties;
1482 44 : oFeatures.Add(oFeature);
1483 44 : oFeature.Add("type", "Feature");
1484 44 : oFeature.Add("properties", oProperties);
1485 44 : oFeature.Add("fid", poFeature->GetFID());
1486 153 : for (int i = 0; i < nFields; ++i)
1487 : {
1488 109 : const auto poFDefn = poFeature->GetFieldDefnRef(i);
1489 109 : const auto eType = poFDefn->GetType();
1490 109 : if (!poFeature->IsFieldSet(i))
1491 0 : continue;
1492 109 : if (poFeature->IsFieldNull(i))
1493 : {
1494 2 : oProperties.SetNull(poFDefn->GetNameRef());
1495 : }
1496 107 : else if (eType == OFTInteger)
1497 : {
1498 1 : if (poFDefn->GetSubType() == OFSTBoolean)
1499 0 : oProperties.Add(
1500 : poFDefn->GetNameRef(),
1501 0 : CPL_TO_BOOL(
1502 : poFeature->GetFieldAsInteger(i)));
1503 : else
1504 1 : oProperties.Add(
1505 : poFDefn->GetNameRef(),
1506 : poFeature->GetFieldAsInteger(i));
1507 : }
1508 106 : else if (eType == OFTInteger64)
1509 : {
1510 33 : oProperties.Add(poFDefn->GetNameRef(),
1511 : poFeature->GetFieldAsInteger64(i));
1512 : }
1513 73 : else if (eType == OFTReal)
1514 : {
1515 33 : oProperties.Add(poFDefn->GetNameRef(),
1516 : poFeature->GetFieldAsDouble(i));
1517 : }
1518 40 : else if ((eType == OFTString &&
1519 45 : poFDefn->GetSubType() != OFSTJSON) ||
1520 80 : eType == OFTDate || eType == OFTTime ||
1521 : eType == OFTDateTime)
1522 : {
1523 35 : oProperties.Add(poFDefn->GetNameRef(),
1524 : poFeature->GetFieldAsString(i));
1525 : }
1526 : else
1527 : {
1528 : char *pszSerialized =
1529 5 : poFeature->GetFieldAsSerializedJSon(i);
1530 5 : if (pszSerialized)
1531 : {
1532 : const auto eStrType =
1533 5 : CPLGetValueType(pszSerialized);
1534 5 : if (eStrType == CPL_VALUE_INTEGER)
1535 : {
1536 1 : oProperties.Add(
1537 : poFDefn->GetNameRef(),
1538 : CPLAtoGIntBig(pszSerialized));
1539 : }
1540 4 : else if (eStrType == CPL_VALUE_REAL)
1541 : {
1542 0 : oProperties.Add(poFDefn->GetNameRef(),
1543 : CPLAtof(pszSerialized));
1544 : }
1545 : else
1546 : {
1547 8 : CPLJSONDocument oDoc;
1548 4 : if (oDoc.LoadMemory(pszSerialized))
1549 4 : oProperties.Add(poFDefn->GetNameRef(),
1550 8 : oDoc.GetRoot());
1551 : }
1552 5 : CPLFree(pszSerialized);
1553 : }
1554 : }
1555 : }
1556 :
1557 84 : const auto GetGeoJSONOptions = [poLayer](int iGeomField)
1558 : {
1559 42 : CPLStringList aosGeoJSONOptions;
1560 : const auto &oCoordPrec =
1561 42 : poLayer->GetLayerDefn()
1562 42 : ->GetGeomFieldDefn(iGeomField)
1563 42 : ->GetCoordinatePrecision();
1564 42 : if (oCoordPrec.dfXYResolution !=
1565 : OGRGeomCoordinatePrecision::UNKNOWN)
1566 : {
1567 : aosGeoJSONOptions.SetNameValue(
1568 : "XY_COORD_PRECISION",
1569 : CPLSPrintf("%d",
1570 : OGRGeomCoordinatePrecision::
1571 : ResolutionToPrecision(
1572 1 : oCoordPrec.dfXYResolution)));
1573 : }
1574 42 : if (oCoordPrec.dfZResolution !=
1575 : OGRGeomCoordinatePrecision::UNKNOWN)
1576 : {
1577 : aosGeoJSONOptions.SetNameValue(
1578 : "Z_COORD_PRECISION",
1579 : CPLSPrintf("%d",
1580 : OGRGeomCoordinatePrecision::
1581 : ResolutionToPrecision(
1582 1 : oCoordPrec.dfZResolution)));
1583 : }
1584 42 : return aosGeoJSONOptions;
1585 44 : };
1586 :
1587 44 : if (nGeomFields == 0)
1588 2 : oFeature.SetNull("geometry");
1589 : else
1590 : {
1591 42 : if (const auto poGeom = poFeature->GetGeometryRef())
1592 : {
1593 : char *pszSerialized =
1594 42 : wkbFlatten(poGeom->getGeometryType()) <=
1595 : wkbGeometryCollection
1596 84 : ? poGeom->exportToJson(
1597 84 : GetGeoJSONOptions(0).List())
1598 42 : : nullptr;
1599 42 : if (pszSerialized)
1600 : {
1601 84 : CPLJSONDocument oDoc;
1602 42 : if (oDoc.LoadMemory(pszSerialized))
1603 42 : oFeature.Add("geometry", oDoc.GetRoot());
1604 42 : CPLFree(pszSerialized);
1605 : }
1606 : else
1607 : {
1608 0 : CPLJSONObject oGeometry;
1609 0 : oFeature.SetNull("geometry");
1610 0 : oFeature.Add("wkt_geometry",
1611 0 : poGeom->exportToWkt());
1612 : }
1613 : }
1614 : else
1615 0 : oFeature.SetNull("geometry");
1616 :
1617 42 : if (nGeomFields > 1)
1618 : {
1619 0 : CPLJSONArray oGeometries;
1620 0 : oFeature.Add("geometries", oGeometries);
1621 0 : for (int i = 0; i < nGeomFields; ++i)
1622 : {
1623 0 : auto poGeom = poFeature->GetGeomFieldRef(i);
1624 0 : if (poGeom)
1625 : {
1626 : char *pszSerialized =
1627 0 : wkbFlatten(poGeom->getGeometryType()) <=
1628 : wkbGeometryCollection
1629 0 : ? poGeom->exportToJson(
1630 0 : GetGeoJSONOptions(i).List())
1631 0 : : nullptr;
1632 0 : if (pszSerialized)
1633 : {
1634 0 : CPLJSONDocument oDoc;
1635 0 : if (oDoc.LoadMemory(pszSerialized))
1636 0 : oGeometries.Add(oDoc.GetRoot());
1637 0 : CPLFree(pszSerialized);
1638 : }
1639 : else
1640 : {
1641 0 : CPLJSONObject oGeometry;
1642 0 : oGeometries.Add(poGeom->exportToWkt());
1643 : }
1644 : }
1645 : else
1646 0 : oGeometries.AddNull();
1647 : }
1648 : }
1649 : }
1650 : }
1651 : else
1652 : {
1653 596 : ConcatStr(
1654 596 : osRet, psOptions->bStdoutOutput,
1655 : poFeature
1656 1192 : ->DumpReadableAsString(psOptions->aosOptions.List())
1657 : .c_str());
1658 : }
1659 : }
1660 88 : }
1661 : }
1662 74 : else if (!bJson && psOptions->nFetchFID != OGRNullFID)
1663 : {
1664 1 : OGRFeature *poFeature = poLayer->GetFeature(psOptions->nFetchFID);
1665 1 : if (poFeature == nullptr)
1666 : {
1667 0 : Concat(osRet, psOptions->bStdoutOutput,
1668 : "Unable to locate feature id " CPL_FRMT_GIB
1669 : " on this layer.\n",
1670 0 : psOptions->nFetchFID);
1671 : }
1672 : else
1673 : {
1674 1 : ConcatStr(
1675 1 : osRet, psOptions->bStdoutOutput,
1676 2 : poFeature->DumpReadableAsString(psOptions->aosOptions.List())
1677 : .c_str());
1678 1 : OGRFeature::DestroyFeature(poFeature);
1679 : }
1680 : }
1681 : }
1682 :
1683 : /************************************************************************/
1684 : /* PrintLayerSummary() */
1685 : /************************************************************************/
1686 :
1687 23 : static void PrintLayerSummary(CPLString &osRet, CPLJSONObject &oLayer,
1688 : const GDALVectorInfoOptions *psOptions,
1689 : OGRLayer *poLayer, bool bIsPrivate)
1690 : {
1691 23 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1692 23 : const bool bIsSummaryCli = psOptions->bIsCli && psOptions->bSummaryOnly;
1693 23 : if (bJson)
1694 2 : oLayer.Set("name", poLayer->GetName());
1695 : else
1696 21 : ConcatStr(osRet, psOptions->bStdoutOutput, poLayer->GetName());
1697 :
1698 23 : const char *pszTitle = poLayer->GetMetadataItem("TITLE");
1699 23 : if (pszTitle)
1700 : {
1701 0 : if (bJson)
1702 0 : oLayer.Set("title", pszTitle);
1703 : else
1704 0 : Concat(osRet, psOptions->bStdoutOutput, " (title: %s)", pszTitle);
1705 : }
1706 :
1707 : const int nGeomFieldCount =
1708 23 : psOptions->bGeomType ? poLayer->GetLayerDefn()->GetGeomFieldCount() : 0;
1709 :
1710 23 : if (bIsSummaryCli && bJson)
1711 : {
1712 2 : CPLJSONArray oGeometryTypes;
1713 7 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1714 : {
1715 : OGRGeomFieldDefn *poGFldDefn =
1716 5 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1717 5 : oGeometryTypes.Add(OGRGeometryTypeToName(poGFldDefn->GetType()));
1718 : }
1719 2 : oLayer.Add("geometryType", oGeometryTypes);
1720 2 : return;
1721 : }
1722 :
1723 21 : if (bJson || nGeomFieldCount > 1)
1724 : {
1725 2 : if (!bJson)
1726 2 : Concat(osRet, psOptions->bStdoutOutput, " (");
1727 4 : CPLJSONArray oGeometryFields;
1728 2 : oLayer.Add("geometryFields", oGeometryFields);
1729 8 : for (int iGeom = 0; iGeom < nGeomFieldCount; iGeom++)
1730 : {
1731 : OGRGeomFieldDefn *poGFldDefn =
1732 6 : poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeom);
1733 6 : if (bJson)
1734 : {
1735 0 : oGeometryFields.Add(
1736 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1737 : }
1738 : else
1739 : {
1740 6 : if (iGeom > 0)
1741 4 : Concat(osRet, psOptions->bStdoutOutput, ", ");
1742 6 : ConcatStr(osRet, psOptions->bStdoutOutput,
1743 : OGRGeometryTypeToName(poGFldDefn->GetType()));
1744 : }
1745 : }
1746 2 : if (!bJson)
1747 4 : Concat(osRet, psOptions->bStdoutOutput, ")");
1748 : }
1749 19 : else if (psOptions->bGeomType && poLayer->GetGeomType() != wkbUnknown)
1750 11 : Concat(osRet, psOptions->bStdoutOutput, " (%s)",
1751 11 : OGRGeometryTypeToName(poLayer->GetGeomType()));
1752 :
1753 21 : if (bIsPrivate)
1754 : {
1755 0 : if (bJson)
1756 0 : oLayer.Set("isPrivate", true);
1757 : else
1758 0 : Concat(osRet, psOptions->bStdoutOutput, " [private]");
1759 : }
1760 :
1761 21 : if (!bJson)
1762 21 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1763 : }
1764 :
1765 : /************************************************************************/
1766 : /* ReportHiearchicalLayers() */
1767 : /************************************************************************/
1768 :
1769 5 : static void ReportHiearchicalLayers(CPLString &osRet, CPLJSONObject &oRoot,
1770 : const GDALVectorInfoOptions *psOptions,
1771 : const GDALGroup *group,
1772 : const std::string &indent, bool bGeomType)
1773 : {
1774 5 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1775 10 : const auto aosVectorLayerNames = group->GetVectorLayerNames();
1776 10 : CPLJSONArray oLayerNames;
1777 5 : oRoot.Add("layerNames", oLayerNames);
1778 23 : for (const auto &osVectorLayerName : aosVectorLayerNames)
1779 : {
1780 18 : OGRLayer *poLayer = group->OpenVectorLayer(osVectorLayerName);
1781 18 : if (poLayer)
1782 : {
1783 36 : CPLJSONObject oLayer;
1784 18 : if (!bJson)
1785 : {
1786 4 : Concat(osRet, psOptions->bStdoutOutput,
1787 : "%sLayer: ", indent.c_str());
1788 4 : PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
1789 : /* bIsPrivate=*/false);
1790 : }
1791 : else
1792 : {
1793 14 : oLayerNames.Add(poLayer->GetName());
1794 : }
1795 : }
1796 : }
1797 :
1798 10 : const std::string subIndent(indent + " ");
1799 10 : auto aosSubGroupNames = group->GetGroupNames();
1800 10 : CPLJSONArray oGroupArray;
1801 5 : oRoot.Add("groups", oGroupArray);
1802 7 : for (const auto &osSubGroupName : aosSubGroupNames)
1803 : {
1804 4 : auto poSubGroup = group->OpenGroup(osSubGroupName);
1805 2 : if (poSubGroup)
1806 : {
1807 4 : CPLJSONObject oGroup;
1808 2 : if (!bJson)
1809 : {
1810 2 : Concat(osRet, psOptions->bStdoutOutput, "Group %s",
1811 : indent.c_str());
1812 2 : Concat(osRet, psOptions->bStdoutOutput, "%s:\n",
1813 : osSubGroupName.c_str());
1814 : }
1815 : else
1816 : {
1817 0 : oGroupArray.Add(oGroup);
1818 0 : oGroup.Set("name", osSubGroupName);
1819 : }
1820 2 : ReportHiearchicalLayers(osRet, oGroup, psOptions, poSubGroup.get(),
1821 : subIndent, bGeomType);
1822 : }
1823 : }
1824 5 : }
1825 :
1826 : /************************************************************************/
1827 : /* GDALVectorInfo() */
1828 : /************************************************************************/
1829 :
1830 : /**
1831 : * Lists various information about a GDAL supported vector dataset.
1832 : *
1833 : * This is the equivalent of the <a href="/programs/ogrinfo.html">ogrinfo</a>
1834 : * utility.
1835 : *
1836 : * GDALVectorInfoOptions* must be allocated and freed with
1837 : * GDALVectorInfoOptionsNew() and GDALVectorInfoOptionsFree() respectively.
1838 : *
1839 : * @param hDataset the dataset handle.
1840 : * @param psOptions the options structure returned by GDALVectorInfoOptionsNew()
1841 : * or NULL.
1842 : * @return string corresponding to the information about the raster dataset
1843 : * (must be freed with CPLFree()), or NULL in case of error.
1844 : *
1845 : * @since GDAL 3.7
1846 : */
1847 103 : char *GDALVectorInfo(GDALDatasetH hDataset,
1848 : const GDALVectorInfoOptions *psOptions)
1849 : {
1850 103 : auto poDS = GDALDataset::FromHandle(hDataset);
1851 103 : if (poDS == nullptr)
1852 0 : return nullptr;
1853 :
1854 206 : const GDALVectorInfoOptions sDefaultOptions;
1855 103 : if (!psOptions)
1856 0 : psOptions = &sDefaultOptions;
1857 :
1858 103 : GDALDriver *poDriver = poDS->GetDriver();
1859 :
1860 206 : CPLString osRet;
1861 206 : CPLJSONObject oRoot;
1862 206 : const std::string osFilename(poDS->GetDescription());
1863 :
1864 103 : const bool bJson = psOptions->eFormat == FORMAT_JSON;
1865 103 : const bool bIsSummaryCli =
1866 103 : psOptions->bIsCli && psOptions->bSummaryUserRequested;
1867 :
1868 206 : CPLJSONArray oLayerArray;
1869 103 : if (bJson)
1870 : {
1871 41 : oRoot.Set("description", poDS->GetDescription());
1872 41 : if (poDriver)
1873 : {
1874 40 : oRoot.Set("driverShortName", poDriver->GetDescription());
1875 40 : oRoot.Set("driverLongName",
1876 80 : poDriver->GetMetadataItem(GDAL_DMD_LONGNAME));
1877 : }
1878 41 : oRoot.Add("layers", oLayerArray);
1879 : }
1880 :
1881 : /* -------------------------------------------------------------------- */
1882 : /* Some information messages. */
1883 : /* -------------------------------------------------------------------- */
1884 103 : if (!bJson && psOptions->bVerbose)
1885 : {
1886 120 : Concat(osRet, psOptions->bStdoutOutput,
1887 : "INFO: Open of `%s'\n"
1888 : " using driver `%s' successful.\n",
1889 : osFilename.c_str(),
1890 60 : poDriver ? poDriver->GetDescription() : "(null)");
1891 : }
1892 :
1893 163 : if (!bJson && psOptions->bVerbose &&
1894 60 : !EQUAL(osFilename.c_str(), poDS->GetDescription()))
1895 : {
1896 0 : Concat(osRet, psOptions->bStdoutOutput,
1897 : "INFO: Internal data source name `%s'\n"
1898 : " different from user name `%s'.\n",
1899 0 : poDS->GetDescription(), osFilename.c_str());
1900 : }
1901 :
1902 103 : int nRepeatCount = psOptions->nRepeatCount;
1903 :
1904 103 : if (!bIsSummaryCli)
1905 : {
1906 99 : GDALVectorInfoReportMetadata(
1907 99 : osRet, oRoot, psOptions, poDS, psOptions->bListMDD,
1908 99 : psOptions->bShowMetadata, psOptions->aosExtraMDDomains.List());
1909 :
1910 99 : CPLJSONObject oDomains;
1911 99 : oRoot.Add("domains", oDomains);
1912 99 : if (!psOptions->osFieldDomain.empty())
1913 : {
1914 7 : auto poDomain = poDS->GetFieldDomain(psOptions->osFieldDomain);
1915 7 : if (poDomain == nullptr)
1916 : {
1917 0 : CPLError(CE_Failure, CPLE_AppDefined,
1918 : "Domain %s cannot be found.",
1919 : psOptions->osFieldDomain.c_str());
1920 0 : return nullptr;
1921 : }
1922 7 : if (!bJson)
1923 7 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1924 7 : ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
1925 7 : if (!bJson)
1926 7 : Concat(osRet, psOptions->bStdoutOutput, "\n");
1927 : }
1928 92 : else if (bJson)
1929 : {
1930 46 : for (const auto &osDomainName : poDS->GetFieldDomainNames())
1931 : {
1932 7 : auto poDomain = poDS->GetFieldDomain(osDomainName);
1933 7 : if (poDomain)
1934 : {
1935 7 : ReportFieldDomain(osRet, oDomains, psOptions, poDomain);
1936 : }
1937 : }
1938 : }
1939 :
1940 99 : if (psOptions->bDatasetGetNextFeature)
1941 : {
1942 1 : nRepeatCount = 0; // skip layer reporting.
1943 :
1944 : /* --------------------------------------------------------------------
1945 : */
1946 : /* Set filters if provided. */
1947 : /* --------------------------------------------------------------------
1948 : */
1949 2 : if (!psOptions->osWHERE.empty() ||
1950 1 : psOptions->poSpatialFilter != nullptr)
1951 : {
1952 0 : for (int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++)
1953 : {
1954 0 : OGRLayer *poLayer = poDS->GetLayer(iLayer);
1955 :
1956 0 : if (poLayer == nullptr)
1957 : {
1958 0 : CPLError(CE_Failure, CPLE_AppDefined,
1959 : "Couldn't fetch advertised layer %d.", iLayer);
1960 0 : return nullptr;
1961 : }
1962 :
1963 0 : if (!psOptions->osWHERE.empty())
1964 : {
1965 0 : if (poLayer->SetAttributeFilter(
1966 0 : psOptions->osWHERE.c_str()) != OGRERR_NONE)
1967 : {
1968 0 : CPLError(
1969 : CE_Warning, CPLE_AppDefined,
1970 : "SetAttributeFilter(%s) failed on layer %s.",
1971 0 : psOptions->osWHERE.c_str(), poLayer->GetName());
1972 : }
1973 : }
1974 :
1975 0 : if (psOptions->poSpatialFilter != nullptr)
1976 : {
1977 0 : if (!psOptions->osGeomField.empty())
1978 : {
1979 0 : OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
1980 0 : const int iGeomField = poDefn->GetGeomFieldIndex(
1981 0 : psOptions->osGeomField.c_str());
1982 0 : if (iGeomField >= 0)
1983 0 : poLayer->SetSpatialFilter(
1984 : iGeomField,
1985 0 : psOptions->poSpatialFilter.get());
1986 : else
1987 0 : CPLError(CE_Warning, CPLE_AppDefined,
1988 : "Cannot find geometry field %s.",
1989 : psOptions->osGeomField.c_str());
1990 : }
1991 : else
1992 : {
1993 0 : poLayer->SetSpatialFilter(
1994 0 : psOptions->poSpatialFilter.get());
1995 : }
1996 : }
1997 : }
1998 : }
1999 :
2000 1 : std::set<OGRLayer *> oSetLayers;
2001 : while (true)
2002 : {
2003 11 : OGRLayer *poLayer = nullptr;
2004 : OGRFeature *poFeature =
2005 11 : poDS->GetNextFeature(&poLayer, nullptr, nullptr, nullptr);
2006 11 : if (poFeature == nullptr)
2007 1 : break;
2008 10 : if (psOptions->aosLayers.empty() || poLayer == nullptr ||
2009 0 : CSLFindString(psOptions->aosLayers.List(),
2010 0 : poLayer->GetName()) >= 0)
2011 : {
2012 10 : if (psOptions->bVerbose && poLayer != nullptr &&
2013 10 : oSetLayers.find(poLayer) == oSetLayers.end())
2014 : {
2015 0 : oSetLayers.insert(poLayer);
2016 0 : CPLJSONObject oLayer;
2017 0 : oLayerArray.Add(oLayer);
2018 0 : ReportOnLayer(
2019 : osRet, oLayer, psOptions, poLayer,
2020 : /*bForceSummary = */ true,
2021 : /*bTakeIntoAccountWHERE = */ false,
2022 : /*bTakeIntoAccountSpatialFilter = */ false,
2023 : /*bTakeIntoAccountGeomField = */ false);
2024 : }
2025 10 : if (!psOptions->bSuperQuiet && !psOptions->bSummaryOnly)
2026 10 : poFeature->DumpReadable(
2027 : nullptr,
2028 : const_cast<char **>(psOptions->aosOptions.List()));
2029 : }
2030 10 : OGRFeature::DestroyFeature(poFeature);
2031 10 : }
2032 : }
2033 :
2034 : /* -------------------------------------------------------------------- */
2035 : /* Special case for -sql clause. No source layers required. */
2036 : /* -------------------------------------------------------------------- */
2037 98 : else if (!psOptions->osSQLStatement.empty())
2038 : {
2039 6 : nRepeatCount = 0; // skip layer reporting.
2040 :
2041 6 : if (!bJson && !psOptions->aosLayers.empty())
2042 0 : Concat(osRet, psOptions->bStdoutOutput,
2043 : "layer names ignored in combination with -sql.\n");
2044 :
2045 6 : CPLErrorReset();
2046 18 : OGRLayer *poResultSet = poDS->ExecuteSQL(
2047 : psOptions->osSQLStatement.c_str(),
2048 6 : psOptions->osGeomField.empty()
2049 6 : ? psOptions->poSpatialFilter.get()
2050 : : nullptr,
2051 6 : psOptions->osDialect.empty() ? nullptr
2052 7 : : psOptions->osDialect.c_str());
2053 :
2054 6 : if (poResultSet != nullptr)
2055 : {
2056 4 : if (!psOptions->osWHERE.empty())
2057 : {
2058 0 : if (poResultSet->SetAttributeFilter(
2059 0 : psOptions->osWHERE.c_str()) != OGRERR_NONE)
2060 : {
2061 0 : CPLError(CE_Failure, CPLE_AppDefined,
2062 : "SetAttributeFilter(%s) failed.",
2063 : psOptions->osWHERE.c_str());
2064 0 : return nullptr;
2065 : }
2066 : }
2067 :
2068 8 : CPLJSONObject oLayer;
2069 4 : oLayerArray.Add(oLayer);
2070 4 : if (!psOptions->osGeomField.empty())
2071 0 : ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
2072 : /*bForceSummary = */ false,
2073 : /*bTakeIntoAccountWHERE = */ false,
2074 : /*bTakeIntoAccountSpatialFilter = */ true,
2075 : /*bTakeIntoAccountGeomField = */ true);
2076 : else
2077 4 : ReportOnLayer(osRet, oLayer, psOptions, poResultSet,
2078 : /*bForceSummary = */ false,
2079 : /*bTakeIntoAccountWHERE = */ false,
2080 : /*bTakeIntoAccountSpatialFilter = */ false,
2081 : /*bTakeIntoAccountGeomField = */ false);
2082 :
2083 4 : poDS->ReleaseResultSet(poResultSet);
2084 : }
2085 2 : else if (CPLGetLastErrorType() != CE_None)
2086 : {
2087 1 : return nullptr;
2088 : }
2089 : }
2090 : }
2091 :
2092 : // coverity[tainted_data]
2093 102 : auto papszLayers = psOptions->aosLayers.List();
2094 197 : for (int iRepeat = 0; iRepeat < nRepeatCount; iRepeat++)
2095 : {
2096 96 : if (papszLayers == nullptr || papszLayers[0] == nullptr)
2097 : {
2098 85 : const int nLayerCount = poDS->GetLayerCount();
2099 85 : if (iRepeat == 0)
2100 85 : CPLDebug("OGR", "GetLayerCount() = %d\n", nLayerCount);
2101 :
2102 85 : bool bDone = false;
2103 85 : auto poRootGroup = poDS->GetRootGroup();
2104 88 : if ((bJson || !psOptions->bAllLayers) && poRootGroup &&
2105 88 : (!poRootGroup->GetGroupNames().empty() ||
2106 87 : !poRootGroup->GetVectorLayerNames().empty()))
2107 : {
2108 6 : CPLJSONObject oGroup;
2109 3 : oRoot.Add("rootGroup", oGroup);
2110 3 : ReportHiearchicalLayers(osRet, oGroup, psOptions,
2111 6 : poRootGroup.get(), std::string(),
2112 3 : psOptions->bGeomType);
2113 3 : if (!bJson)
2114 1 : bDone = true;
2115 : }
2116 :
2117 : /* --------------------------------------------------------------------
2118 : */
2119 : /* Process each data source layer. */
2120 : /* --------------------------------------------------------------------
2121 : */
2122 251 : for (int iLayer = 0; !bDone && iLayer < nLayerCount; iLayer++)
2123 : {
2124 166 : OGRLayer *poLayer = poDS->GetLayer(iLayer);
2125 :
2126 166 : if (poLayer == nullptr)
2127 : {
2128 0 : CPLError(CE_Failure, CPLE_AppDefined,
2129 : "Couldn't fetch advertised layer %d.", iLayer);
2130 0 : return nullptr;
2131 : }
2132 :
2133 332 : CPLJSONObject oLayer;
2134 166 : oLayerArray.Add(oLayer);
2135 166 : if (!psOptions->bAllLayers || bIsSummaryCli)
2136 : {
2137 19 : if (!bJson)
2138 17 : Concat(osRet, psOptions->bStdoutOutput,
2139 : "%d: ", iLayer + 1);
2140 19 : PrintLayerSummary(osRet, oLayer, psOptions, poLayer,
2141 19 : poDS->IsLayerPrivate(iLayer));
2142 : }
2143 : else
2144 : {
2145 147 : if (iRepeat != 0)
2146 0 : poLayer->ResetReading();
2147 :
2148 147 : ReportOnLayer(osRet, oLayer, psOptions, poLayer,
2149 : /*bForceSummary = */ false,
2150 : /*bTakeIntoAccountWHERE = */ true,
2151 : /*bTakeIntoAccountSpatialFilter = */ true,
2152 : /*bTakeIntoAccountGeomField = */ true);
2153 : }
2154 85 : }
2155 : }
2156 : else
2157 : {
2158 : /* --------------------------------------------------------------------
2159 : */
2160 : /* Process specified data source layers. */
2161 : /* --------------------------------------------------------------------
2162 : */
2163 :
2164 22 : for (const char *pszLayer : cpl::Iterate(papszLayers))
2165 : {
2166 12 : OGRLayer *poLayer = poDS->GetLayerByName(pszLayer);
2167 :
2168 12 : if (poLayer == nullptr)
2169 : {
2170 1 : CPLError(CE_Failure, CPLE_AppDefined,
2171 : "Couldn't fetch requested layer %s.", pszLayer);
2172 1 : return nullptr;
2173 : }
2174 :
2175 11 : if (iRepeat != 0)
2176 0 : poLayer->ResetReading();
2177 :
2178 22 : CPLJSONObject oLayer;
2179 11 : oLayerArray.Add(oLayer);
2180 11 : ReportOnLayer(osRet, oLayer, psOptions, poLayer,
2181 : /*bForceSummary = */ false,
2182 : /*bTakeIntoAccountWHERE = */ true,
2183 : /*bTakeIntoAccountSpatialFilter = */ true,
2184 : /*bTakeIntoAccountGeomField = */ true);
2185 : }
2186 : }
2187 : }
2188 :
2189 101 : if (!papszLayers && !bIsSummaryCli)
2190 : {
2191 87 : ReportRelationships(osRet, oRoot, psOptions, poDS);
2192 : }
2193 :
2194 101 : if (bJson)
2195 : {
2196 40 : osRet.clear();
2197 40 : ConcatStr(
2198 40 : osRet, psOptions->bStdoutOutput,
2199 : json_object_to_json_string_ext(
2200 40 : static_cast<struct json_object *>(oRoot.GetInternalHandle()),
2201 : JSON_C_TO_STRING_PRETTY
2202 : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
2203 : | JSON_C_TO_STRING_NOSLASHESCAPE
2204 : #endif
2205 : ));
2206 40 : ConcatStr(osRet, psOptions->bStdoutOutput, "\n");
2207 : }
2208 :
2209 101 : return VSI_STRDUP_VERBOSE(osRet);
2210 : }
2211 :
2212 : /************************************************************************/
2213 : /* GDALVectorInfoOptionsGetParser() */
2214 : /************************************************************************/
2215 :
2216 109 : static std::unique_ptr<GDALArgumentParser> GDALVectorInfoOptionsGetParser(
2217 : GDALVectorInfoOptions *psOptions,
2218 : GDALVectorInfoOptionsForBinary *psOptionsForBinary)
2219 : {
2220 : auto argParser = std::make_unique<GDALArgumentParser>(
2221 109 : "ogrinfo", /* bForBinary=*/psOptionsForBinary != nullptr);
2222 :
2223 109 : argParser->add_description(
2224 109 : _("Lists information about an OGR-supported data source."));
2225 :
2226 109 : argParser->add_epilog(
2227 109 : _("For more details, consult https://gdal.org/programs/ogrinfo.html"));
2228 :
2229 109 : argParser->add_argument("-json")
2230 109 : .flag()
2231 41 : .action([psOptions](const std::string &)
2232 109 : { psOptions->eFormat = FORMAT_JSON; })
2233 109 : .help(_("Display the output in json format."));
2234 :
2235 109 : argParser->add_argument("-ro")
2236 109 : .flag()
2237 : .action(
2238 14 : [psOptionsForBinary](const std::string &)
2239 : {
2240 7 : if (psOptionsForBinary)
2241 7 : psOptionsForBinary->bReadOnly = true;
2242 109 : })
2243 109 : .help(_("Open the data source in read-only mode."));
2244 :
2245 109 : argParser->add_argument("-update")
2246 109 : .flag()
2247 : .action(
2248 0 : [psOptionsForBinary](const std::string &)
2249 : {
2250 0 : if (psOptionsForBinary)
2251 0 : psOptionsForBinary->bUpdate = true;
2252 109 : })
2253 109 : .help(_("Open the data source in update mode."));
2254 :
2255 109 : argParser->add_argument("-q", "--quiet")
2256 109 : .flag()
2257 : .action(
2258 4 : [psOptions, psOptionsForBinary](const std::string &)
2259 : {
2260 2 : psOptions->bVerbose = false;
2261 2 : if (psOptionsForBinary)
2262 2 : psOptionsForBinary->bVerbose = false;
2263 109 : })
2264 : .help(_("Quiet mode. No progress message is emitted on the standard "
2265 109 : "output."));
2266 :
2267 : #ifdef __AFL_HAVE_MANUAL_CONTROL
2268 : /* Undocumented: mainly only useful for AFL testing */
2269 : argParser->add_argument("-qq")
2270 : .flag()
2271 : .hidden()
2272 : .action(
2273 : [psOptions, psOptionsForBinary](const std::string &)
2274 : {
2275 : psOptions->bVerbose = false;
2276 : if (psOptionsForBinary)
2277 : psOptionsForBinary->bVerbose = false;
2278 : psOptions->bSuperQuiet = true;
2279 : })
2280 : .help(_("Super quiet mode."));
2281 : #endif
2282 :
2283 109 : argParser->add_argument("-fid")
2284 218 : .metavar("<FID>")
2285 109 : .store_into(psOptions->nFetchFID)
2286 109 : .help(_("Only the feature with this feature id will be reported."));
2287 :
2288 109 : argParser->add_argument("-spat")
2289 218 : .metavar("<xmin> <ymin> <xmax> <ymax>")
2290 109 : .nargs(4)
2291 109 : .scan<'g', double>()
2292 : .help(_("The area of interest. Only features within the rectangle will "
2293 109 : "be reported."));
2294 :
2295 109 : argParser->add_argument("-geomfield")
2296 218 : .metavar("<field>")
2297 109 : .store_into(psOptions->osGeomField)
2298 : .help(_("Name of the geometry field on which the spatial filter "
2299 109 : "operates."));
2300 :
2301 109 : argParser->add_argument("-where")
2302 218 : .metavar("<restricted_where>")
2303 109 : .store_into(psOptions->osWHERE)
2304 : .help(_("An attribute query in a restricted form of the queries used "
2305 109 : "in the SQL WHERE statement."));
2306 :
2307 : {
2308 109 : auto &group = argParser->add_mutually_exclusive_group();
2309 109 : group.add_argument("-sql")
2310 218 : .metavar("<statement|@filename>")
2311 109 : .store_into(psOptions->osSQLStatement)
2312 : .help(_(
2313 109 : "Execute the indicated SQL statement and return the result."));
2314 :
2315 109 : group.add_argument("-rl")
2316 109 : .store_into(psOptions->bDatasetGetNextFeature)
2317 109 : .help(_("Enable random layer reading mode."));
2318 : }
2319 :
2320 109 : argParser->add_argument("-dialect")
2321 218 : .metavar("<dialect>")
2322 109 : .store_into(psOptions->osDialect)
2323 109 : .help(_("SQL dialect."));
2324 :
2325 : // Only for fuzzing
2326 109 : argParser->add_argument("-rc")
2327 109 : .hidden()
2328 218 : .metavar("<count>")
2329 109 : .store_into(psOptions->nRepeatCount)
2330 109 : .help(_("Repeat count"));
2331 :
2332 109 : argParser->add_argument("-al")
2333 109 : .store_into(psOptions->bAllLayers)
2334 : .help(_("List all layers (used instead of having to give layer names "
2335 109 : "as arguments)."));
2336 :
2337 : {
2338 109 : auto &group = argParser->add_mutually_exclusive_group();
2339 109 : group.add_argument("-so", "-summary")
2340 109 : .store_into(psOptions->bSummaryUserRequested)
2341 : .help(_("Summary only: show only summary information like "
2342 109 : "projection, schema, feature count and extents."));
2343 :
2344 109 : group.add_argument("-features")
2345 109 : .store_into(psOptions->bFeaturesUserRequested)
2346 109 : .help(_("Enable listing of features."));
2347 : }
2348 :
2349 109 : argParser->add_argument("-limit")
2350 218 : .metavar("<nb_features>")
2351 109 : .store_into(psOptions->nLimit)
2352 109 : .help(_("Limit the number of features per layer."));
2353 :
2354 109 : argParser->add_argument("-fields")
2355 109 : .choices("YES", "NO")
2356 218 : .metavar("YES|NO")
2357 : .action(
2358 2 : [psOptions](const std::string &s) {
2359 2 : psOptions->aosOptions.SetNameValue("DISPLAY_FIELDS", s.c_str());
2360 109 : })
2361 : .help(
2362 109 : _("If set to NO, the feature dump will not display field values."));
2363 :
2364 109 : argParser->add_argument("-geom")
2365 109 : .choices("YES", "NO", "SUMMARY", "WKT", "ISO_WKT")
2366 218 : .metavar("YES|NO|SUMMARY|WKT|ISO_WKT")
2367 : .action(
2368 3 : [psOptions](const std::string &s) {
2369 : psOptions->aosOptions.SetNameValue("DISPLAY_GEOMETRY",
2370 3 : s.c_str());
2371 109 : })
2372 109 : .help(_("How to display geometries in feature dump."));
2373 :
2374 109 : argParser->add_argument("-oo")
2375 109 : .append()
2376 218 : .metavar("<NAME=VALUE>")
2377 : .action(
2378 20 : [psOptionsForBinary](const std::string &s)
2379 : {
2380 10 : if (psOptionsForBinary)
2381 10 : psOptionsForBinary->aosOpenOptions.AddString(s.c_str());
2382 109 : })
2383 109 : .help(_("Dataset open option (format-specific)."));
2384 :
2385 109 : argParser->add_argument("-nomd")
2386 109 : .flag()
2387 1 : .action([psOptions](const std::string &)
2388 109 : { psOptions->bShowMetadata = false; })
2389 109 : .help(_("Suppress metadata printing."));
2390 :
2391 109 : argParser->add_argument("-listmdd")
2392 109 : .store_into(psOptions->bListMDD)
2393 109 : .help(_("List all metadata domains available for the dataset."));
2394 :
2395 109 : argParser->add_argument("-mdd")
2396 109 : .append()
2397 218 : .metavar("<domain>")
2398 1 : .action([psOptions](const std::string &s)
2399 110 : { psOptions->aosExtraMDDomains.AddString(s.c_str()); })
2400 109 : .help(_("List metadata in the specified domain."));
2401 :
2402 109 : argParser->add_argument("-nocount")
2403 109 : .flag()
2404 2 : .action([psOptions](const std::string &)
2405 109 : { psOptions->bFeatureCount = false; })
2406 109 : .help(_("Suppress feature count printing."));
2407 :
2408 109 : argParser->add_argument("-noextent")
2409 109 : .flag()
2410 0 : .action([psOptions](const std::string &)
2411 109 : { psOptions->bExtent = false; })
2412 109 : .help(_("Suppress spatial extent printing."));
2413 :
2414 109 : argParser->add_argument("-extent3D")
2415 109 : .store_into(psOptions->bExtent3D)
2416 109 : .help(_("Request a 3D extent to be reported."));
2417 :
2418 109 : argParser->add_argument("-nogeomtype")
2419 109 : .flag()
2420 1 : .action([psOptions](const std::string &)
2421 109 : { psOptions->bGeomType = false; })
2422 109 : .help(_("Suppress layer geometry type printing."));
2423 :
2424 109 : argParser->add_argument("-wkt_format")
2425 109 : .store_into(psOptions->osWKTFormat)
2426 218 : .metavar("WKT1|WKT2|WKT2_2015|WKT2_2019")
2427 109 : .help(_("The WKT format used to display the SRS."));
2428 :
2429 109 : argParser->add_argument("-fielddomain")
2430 109 : .store_into(psOptions->osFieldDomain)
2431 218 : .metavar("<name>")
2432 109 : .help(_("Display details about a field domain."));
2433 :
2434 109 : argParser->add_argument("-if")
2435 109 : .append()
2436 218 : .metavar("<format>")
2437 : .action(
2438 4 : [psOptionsForBinary](const std::string &s)
2439 : {
2440 2 : if (psOptionsForBinary)
2441 : {
2442 2 : if (GDALGetDriverByName(s.c_str()) == nullptr)
2443 : {
2444 0 : CPLError(CE_Warning, CPLE_AppDefined,
2445 : "%s is not a recognized driver", s.c_str());
2446 : }
2447 : psOptionsForBinary->aosAllowInputDrivers.AddString(
2448 2 : s.c_str());
2449 : }
2450 109 : })
2451 109 : .help(_("Format/driver name(s) to try when opening the input file."));
2452 :
2453 109 : argParser->add_argument("-stdout")
2454 109 : .flag()
2455 109 : .store_into(psOptions->bStdoutOutput)
2456 109 : .hidden()
2457 109 : .help(_("Directly output on stdout (format=text mode only)"));
2458 :
2459 109 : argParser->add_argument("--cli")
2460 109 : .hidden()
2461 109 : .store_into(psOptions->bIsCli)
2462 : .help(_("Indicates that this is called from the gdal vector info CLI "
2463 109 : "utility."));
2464 :
2465 109 : auto &argFilename = argParser->add_argument("filename")
2466 : .action(
2467 117 : [psOptionsForBinary](const std::string &s)
2468 : {
2469 74 : if (psOptionsForBinary)
2470 43 : psOptionsForBinary->osFilename = s;
2471 109 : })
2472 109 : .help(_("The data source to open."));
2473 109 : if (!psOptionsForBinary)
2474 64 : argFilename.nargs(argparse::nargs_pattern::optional);
2475 :
2476 109 : argParser->add_argument("layer")
2477 109 : .remaining()
2478 218 : .metavar("<layer_name>")
2479 109 : .help(_("Layer name."));
2480 :
2481 109 : return argParser;
2482 : }
2483 :
2484 : /************************************************************************/
2485 : /* GDALVectorInfoGetParserUsage() */
2486 : /************************************************************************/
2487 :
2488 1 : std::string GDALVectorInfoGetParserUsage()
2489 : {
2490 : try
2491 : {
2492 2 : GDALVectorInfoOptions sOptions;
2493 2 : GDALVectorInfoOptionsForBinary sOptionsForBinary;
2494 : auto argParser =
2495 2 : GDALVectorInfoOptionsGetParser(&sOptions, &sOptionsForBinary);
2496 1 : return argParser->usage();
2497 : }
2498 0 : catch (const std::exception &err)
2499 : {
2500 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
2501 0 : err.what());
2502 0 : return std::string();
2503 : }
2504 : }
2505 :
2506 : /************************************************************************/
2507 : /* GDALVectorInfoOptionsNew() */
2508 : /************************************************************************/
2509 :
2510 : /**
2511 : * Allocates a GDALVectorInfoOptions struct.
2512 : *
2513 : * Note that when this function is used a library function, and not from the
2514 : * ogrinfo utility, a dataset name must be specified if any layer names(s) are
2515 : * specified (if no layer name is specific, passing a dataset name is not
2516 : * needed). That dataset name may be a dummy one, as the dataset taken into
2517 : * account is the hDS parameter passed to GDALVectorInfo().
2518 : * Similarly the -oo switch in a non-ogrinfo context will be ignored, and it
2519 : * is the responsibility of the user to apply them when opening the hDS parameter
2520 : * passed to GDALVectorInfo().
2521 : *
2522 : * @param papszArgv NULL terminated list of options (potentially including
2523 : * filename and open options too), or NULL. The accepted options are the ones of
2524 : * the <a href="/programs/ogrinfo.html">ogrinfo</a> utility.
2525 : * @param psOptionsForBinary (output) may be NULL (and should generally be
2526 : * NULL), otherwise (ogrinfo_bin.cpp use case) must be allocated with
2527 : * GDALVectorInfoOptionsForBinaryNew() prior to this
2528 : * function. Will be filled with potentially present filename, open options,
2529 : * subdataset number...
2530 : * @return pointer to the allocated GDALVectorInfoOptions struct. Must be freed
2531 : * with GDALVectorInfoOptionsFree().
2532 : *
2533 : * @since GDAL 3.7
2534 : */
2535 :
2536 : GDALVectorInfoOptions *
2537 108 : GDALVectorInfoOptionsNew(char **papszArgv,
2538 : GDALVectorInfoOptionsForBinary *psOptionsForBinary)
2539 : {
2540 216 : auto psOptions = std::make_unique<GDALVectorInfoOptions>();
2541 :
2542 : try
2543 : {
2544 : auto argParser =
2545 216 : GDALVectorInfoOptionsGetParser(psOptions.get(), psOptionsForBinary);
2546 :
2547 : /* Special pre-processing to rewrite -fields=foo as "-fields" "FOO", and
2548 : * same for -geom=foo. */
2549 216 : CPLStringList aosArgv;
2550 472 : for (CSLConstList papszIter = papszArgv; papszIter && *papszIter;
2551 : ++papszIter)
2552 : {
2553 364 : if (STARTS_WITH(*papszIter, "-fields="))
2554 : {
2555 2 : aosArgv.AddString("-fields");
2556 : aosArgv.AddString(
2557 2 : CPLString(*papszIter + strlen("-fields=")).toupper());
2558 : }
2559 362 : else if (STARTS_WITH(*papszIter, "-geom="))
2560 : {
2561 3 : aosArgv.AddString("-geom");
2562 : aosArgv.AddString(
2563 3 : CPLString(*papszIter + strlen("-geom=")).toupper());
2564 : }
2565 : else
2566 : {
2567 359 : aosArgv.AddString(*papszIter);
2568 : }
2569 : }
2570 :
2571 108 : argParser->parse_args_without_binary_name(aosArgv.List());
2572 :
2573 214 : auto layers = argParser->present<std::vector<std::string>>("layer");
2574 107 : if (layers)
2575 : {
2576 23 : for (const auto &layer : *layers)
2577 : {
2578 12 : psOptions->aosLayers.AddString(layer.c_str());
2579 12 : psOptions->bAllLayers = false;
2580 : }
2581 : }
2582 :
2583 109 : if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
2584 : {
2585 2 : const double dfMinX = (*oSpat)[0];
2586 2 : const double dfMinY = (*oSpat)[1];
2587 2 : const double dfMaxX = (*oSpat)[2];
2588 2 : const double dfMaxY = (*oSpat)[3];
2589 :
2590 : auto poPolygon =
2591 4 : std::make_unique<OGRPolygon>(dfMinX, dfMinY, dfMaxX, dfMaxY);
2592 2 : psOptions->poSpatialFilter.reset(poPolygon.release());
2593 : }
2594 :
2595 107 : if (!psOptions->osWHERE.empty() && psOptions->osWHERE[0] == '@')
2596 : {
2597 0 : GByte *pabyRet = nullptr;
2598 0 : if (VSIIngestFile(nullptr, psOptions->osWHERE.substr(1).c_str(),
2599 0 : &pabyRet, nullptr, 10 * 1024 * 1024))
2600 : {
2601 0 : GDALRemoveBOM(pabyRet);
2602 0 : psOptions->osWHERE = reinterpret_cast<const char *>(pabyRet);
2603 0 : VSIFree(pabyRet);
2604 : }
2605 : else
2606 : {
2607 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2608 0 : psOptions->osWHERE.substr(1).c_str());
2609 0 : return nullptr;
2610 : }
2611 : }
2612 :
2613 113 : if (!psOptions->osSQLStatement.empty() &&
2614 6 : psOptions->osSQLStatement[0] == '@')
2615 : {
2616 1 : GByte *pabyRet = nullptr;
2617 1 : if (VSIIngestFile(nullptr,
2618 2 : psOptions->osSQLStatement.substr(1).c_str(),
2619 1 : &pabyRet, nullptr, 10 * 1024 * 1024))
2620 : {
2621 1 : GDALRemoveBOM(pabyRet);
2622 1 : char *pszSQLStatement = reinterpret_cast<char *>(pabyRet);
2623 1 : psOptions->osSQLStatement =
2624 2 : CPLRemoveSQLComments(pszSQLStatement);
2625 1 : VSIFree(pabyRet);
2626 : }
2627 : else
2628 : {
2629 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2630 0 : psOptions->osSQLStatement.substr(1).c_str());
2631 0 : return nullptr;
2632 : }
2633 : }
2634 :
2635 107 : if (psOptionsForBinary)
2636 : {
2637 43 : psOptions->bStdoutOutput = true;
2638 43 : psOptionsForBinary->osSQLStatement = psOptions->osSQLStatement;
2639 : }
2640 :
2641 107 : if (psOptions->eFormat == FORMAT_JSON)
2642 : {
2643 41 : psOptions->bAllLayers = true;
2644 41 : psOptions->bSummaryOnly = true;
2645 41 : if (psOptions->aosExtraMDDomains.empty())
2646 41 : psOptions->aosExtraMDDomains.AddString("all");
2647 41 : psOptions->bStdoutOutput = false;
2648 : }
2649 :
2650 107 : if (psOptions->bSummaryUserRequested)
2651 17 : psOptions->bSummaryOnly = true;
2652 90 : else if (psOptions->bFeaturesUserRequested)
2653 12 : psOptions->bSummaryOnly = false;
2654 :
2655 107 : 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 107 : return psOptions.release();
2663 : }
2664 1 : catch (const std::exception &err)
2665 : {
2666 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
2667 1 : return nullptr;
2668 : }
2669 : }
|