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