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