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