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